QGIS API Documentation 3.39.0-Master (d85f3c2a281)
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
6 email : matthias@opengis.ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
17#include <random>
18
20#include "qgscoordinateutils.h"
22#include "qgsexpressionutils.h"
24#include "qgsexiftools.h"
25#include "qgsfeaturerequest.h"
26#include "qgsgeos.h"
27#include "qgsstringutils.h"
28#include "qgsmultipoint.h"
29#include "qgsgeometryutils.h"
30#include "qgshstoreutils.h"
31#include "qgsmultilinestring.h"
32#include "qgslinestring.h"
33#include "qgscurvepolygon.h"
35#include "qgspolygon.h"
36#include "qgstriangle.h"
37#include "qgscurve.h"
38#include "qgsregularpolygon.h"
39#include "qgsquadrilateral.h"
40#include "qgsvariantutils.h"
41#include "qgsogcutils.h"
42#include "qgsdistancearea.h"
43#include "qgsgeometryengine.h"
45#include "qgssymbollayerutils.h"
46#include "qgsstyle.h"
47#include "qgsexception.h"
48#include "qgsmessagelog.h"
49#include "qgsrasterlayer.h"
50#include "qgsvectorlayer.h"
51#include "qgsvectorlayerutils.h"
52#include "qgsrasterbandstats.h"
53#include "qgscolorramp.h"
55#include "qgsfieldformatter.h"
57#include "qgsproviderregistry.h"
58#include "sqlite3.h"
59#include "qgstransaction.h"
60#include "qgsthreadingutils.h"
61#include "qgsapplication.h"
62#include "qgis.h"
64#include "qgsunittypes.h"
65#include "qgsspatialindex.h"
66#include "qgscolorrampimpl.h"
67
68#include <QMimeDatabase>
69#include <QProcessEnvironment>
70#include <QCryptographicHash>
71#include <QRegularExpression>
72#include <QUuid>
73#include <QUrlQuery>
74
75typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
76
78Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
80
83Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
84
85const QString QgsExpressionFunction::helpText() const
86{
87 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
88}
89
91{
92 Q_UNUSED( node )
93 // evaluate arguments
94 QVariantList argValues;
95 if ( args )
96 {
97 int arg = 0;
98 const QList< QgsExpressionNode * > argList = args->list();
99 for ( QgsExpressionNode *n : argList )
100 {
101 QVariant v;
102 if ( lazyEval() )
103 {
104 // Pass in the node for the function to eval as it needs.
105 v = QVariant::fromValue( n );
106 }
107 else
108 {
109 v = n->eval( parent, context );
111 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
112 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
113 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
114 }
115 argValues.append( v );
116 arg++;
117 }
118 }
119
120 return func( argValues, context, parent, node );
121}
122
124{
125 Q_UNUSED( node )
126 return true;
127}
128
130{
131 return QStringList();
132}
133
135{
136 Q_UNUSED( parent )
137 Q_UNUSED( context )
138 Q_UNUSED( node )
139 return false;
140}
141
143{
144 Q_UNUSED( parent )
145 Q_UNUSED( context )
146 Q_UNUSED( node )
147 return true;
148}
149
151{
152 Q_UNUSED( node )
153 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
154}
155
157{
158 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
159}
160
162{
163 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
164}
165
167{
168 return mHandlesNull;
169}
170
171// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
174 FcnEval fcn,
175 const QString &group,
176 const QString &helpText,
177 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
178 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
179 bool lazyEval,
180 const QStringList &aliases,
181 bool handlesNull )
182 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
183 , mFnc( fcn )
184 , mAliases( aliases )
185 , mUsesGeometry( false )
186 , mUsesGeometryFunc( usesGeometry )
187 , mReferencedColumnsFunc( referencedColumns )
188{
189}
191
193{
194 return mAliases;
195}
196
198{
199 if ( mUsesGeometryFunc )
200 return mUsesGeometryFunc( node );
201 else
202 return mUsesGeometry;
203}
204
205void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
206{
207 mUsesGeometryFunc = usesGeometry;
208}
209
211{
212 if ( mReferencedColumnsFunc )
213 return mReferencedColumnsFunc( node );
214 else
215 return mReferencedColumns;
216}
217
219{
220 if ( mIsStaticFunc )
221 return mIsStaticFunc( node, parent, context );
222 else
223 return mIsStatic;
224}
225
227{
228 if ( mPrepareFunc )
229 return mPrepareFunc( node, parent, context );
230
231 return true;
232}
233
235{
236 mIsStaticFunc = isStatic;
237}
238
240{
241 mIsStaticFunc = nullptr;
242 mIsStatic = isStatic;
243}
244
245void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
246{
247 mPrepareFunc = prepareFunc;
248}
249
251{
252 if ( node && node->args() )
253 {
254 const QList< QgsExpressionNode * > argList = node->args()->list();
255 for ( QgsExpressionNode *argNode : argList )
256 {
257 if ( !argNode->isStatic( parent, context ) )
258 return false;
259 }
260 }
261
262 return true;
263}
264
265static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
266{
267 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
268 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
269 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
270
271 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
272 return QVariant();
273
274 QVariantList array;
275 int length = 1;
276
277 array << start;
278 double current = start + step;
279 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
280 {
281 array << current;
282 current += step;
283 length++;
284 }
285
286 return array;
287}
288
289static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
290{
291 if ( !context )
292 return QVariant();
293
294 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
295
296 if ( name == QLatin1String( "feature" ) )
297 {
298 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
299 }
300 else if ( name == QLatin1String( "id" ) )
301 {
302 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
303 }
304 else if ( name == QLatin1String( "geometry" ) )
305 {
306 if ( !context->hasFeature() )
307 return QVariant();
308
309 const QgsFeature feature = context->feature();
310 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
311 }
312 else
313 {
314 return context->variable( name );
315 }
316}
317
318static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
319{
320 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
321 return QgsExpression::replaceExpressionText( templateString, context );
322}
323
324static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
325{
326 if ( !context )
327 return QVariant();
328
329 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
330 QgsExpression expression( expString );
331 return expression.evaluate( context );
332}
333
334static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
335{
336 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
337 return QVariant( std::sqrt( x ) );
338}
339
340static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341{
342 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343 return QVariant( std::fabs( val ) );
344}
345
346static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
347{
348 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
349 return ( deg * M_PI ) / 180;
350}
351static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
352{
353 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
354 return ( 180 * rad ) / M_PI;
355}
356static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
357{
358 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
359 return QVariant( std::sin( x ) );
360}
361static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
362{
363 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
364 return QVariant( std::cos( x ) );
365}
366static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
367{
368 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
369 return QVariant( std::tan( x ) );
370}
371static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
372{
373 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
374 return QVariant( std::asin( x ) );
375}
376static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
377{
378 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
379 return QVariant( std::acos( x ) );
380}
381static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
382{
383 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
384 return QVariant( std::atan( x ) );
385}
386static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
387{
388 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
389 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
390 return QVariant( std::atan2( y, x ) );
391}
392static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
393{
394 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
395 return QVariant( std::exp( x ) );
396}
397static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
398{
399 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
400 if ( x <= 0 )
401 return QVariant();
402 return QVariant( std::log( x ) );
403}
404static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 if ( x <= 0 )
408 return QVariant();
409 return QVariant( log10( x ) );
410}
411static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
412{
413 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
414 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
415 if ( x <= 0 || b <= 0 )
416 return QVariant();
417 return QVariant( std::log( x ) / std::log( b ) );
418}
419static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
420{
421 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
422 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
423 if ( max < min )
424 return QVariant();
425
426 std::random_device rd;
427 std::mt19937_64 generator( rd() );
428
429 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
430 {
431 quint32 seed;
432 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
433 {
434 // if seed can be converted to int, we use as is
435 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
436 }
437 else
438 {
439 // if not, we hash string representation to int
440 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
441 std::hash<std::string> hasher;
442 seed = hasher( seedStr.toStdString() );
443 }
444 generator.seed( seed );
445 }
446
447 // Return a random double in the range [min, max] (inclusive)
448 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
449 return QVariant( min + f * ( max - min ) );
450}
451static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
452{
453 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
454 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
455 if ( max < min )
456 return QVariant();
457
458 std::random_device rd;
459 std::mt19937_64 generator( rd() );
460
461 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
462 {
463 quint32 seed;
464 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
465 {
466 // if seed can be converted to int, we use as is
467 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
468 }
469 else
470 {
471 // if not, we hash string representation to int
472 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
473 std::hash<std::string> hasher;
474 seed = hasher( seedStr.toStdString() );
475 }
476 generator.seed( seed );
477 }
478
479 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
480 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
481 return QVariant( randomInteger );
482
483 // Prevent wrong conversion of QVariant. See #36412
484 return QVariant( int( randomInteger ) );
485}
486
487static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
488{
489 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
490 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
491 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
492 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
493 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
494
495 if ( domainMin >= domainMax )
496 {
497 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
498 return QVariant();
499 }
500
501 // outside of domain?
502 if ( val >= domainMax )
503 {
504 return rangeMax;
505 }
506 else if ( val <= domainMin )
507 {
508 return rangeMin;
509 }
510
511 // calculate linear scale
512 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
513 double c = rangeMin - ( domainMin * m );
514
515 // Return linearly scaled value
516 return QVariant( m * val + c );
517}
518
519static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
520{
521 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
522 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
523 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
524 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
525 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
526 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
527
528 if ( domainMin >= domainMax )
529 {
530 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
531 return QVariant();
532 }
533 if ( exponent <= 0 )
534 {
535 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
536 return QVariant();
537 }
538
539 // outside of domain?
540 if ( val >= domainMax )
541 {
542 return rangeMax;
543 }
544 else if ( val <= domainMin )
545 {
546 return rangeMin;
547 }
548
549 // Return polynomially scaled value
550 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
551}
552
553static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
554{
555 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
556 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
557 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
558 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
559 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
560 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
561
562 if ( domainMin >= domainMax )
563 {
564 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
565 return QVariant();
566 }
567 if ( exponent <= 0 )
568 {
569 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
570 return QVariant();
571 }
572
573 // outside of domain?
574 if ( val >= domainMax )
575 {
576 return rangeMax;
577 }
578 else if ( val <= domainMin )
579 {
580 return rangeMin;
581 }
582
583 // Return exponentially scaled value
584 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
585 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
586}
587
588static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
589{
590 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
591 double maxVal = std::numeric_limits<double>::quiet_NaN();
592 for ( const QVariant &val : values )
593 {
594 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
595 if ( std::isnan( maxVal ) )
596 {
597 maxVal = testVal;
598 }
599 else if ( !std::isnan( testVal ) )
600 {
601 maxVal = std::max( maxVal, testVal );
602 }
603 }
604
605 if ( !std::isnan( maxVal ) )
606 {
607 result = QVariant( maxVal );
608 }
609 return result;
610}
611
612static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
613{
614 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
615 double minVal = std::numeric_limits<double>::quiet_NaN();
616 for ( const QVariant &val : values )
617 {
618 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
619 if ( std::isnan( minVal ) )
620 {
621 minVal = testVal;
622 }
623 else if ( !std::isnan( testVal ) )
624 {
625 minVal = std::min( minVal, testVal );
626 }
627 }
628
629 if ( !std::isnan( minVal ) )
630 {
631 result = QVariant( minVal );
632 }
633 return result;
634}
635
636static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
637{
638 //lazy eval, so we need to evaluate nodes now
639
640 //first node is layer id or name
641 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
643 QVariant value = node->eval( parent, context );
645
646 // TODO this expression function is NOT thread safe
648 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
650 if ( !vl )
651 {
652 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
653 return QVariant();
654 }
655
656 // second node is aggregate type
657 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
659 value = node->eval( parent, context );
661 bool ok = false;
662 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
663 if ( !ok )
664 {
665 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
666 return QVariant();
667 }
668
669 // third node is subexpression (or field name)
670 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
672 QString subExpression = node->dump();
673
675 //optional forth node is filter
676 if ( values.count() > 3 )
677 {
678 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
680 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
681 if ( !nl || nl->value().isValid() )
682 parameters.filter = node->dump();
683 }
684
685 //optional fifth node is concatenator
686 if ( values.count() > 4 )
687 {
688 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
690 value = node->eval( parent, context );
692 parameters.delimiter = value.toString();
693 }
694
695 //optional sixth node is order by
696 QString orderBy;
697 if ( values.count() > 5 )
698 {
699 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
701 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
702 if ( !nl || nl->value().isValid() )
703 {
704 orderBy = node->dump();
705 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
706 }
707 }
708
709 QString aggregateError;
710 QVariant result;
711 if ( context )
712 {
713 QString cacheKey;
714 QgsExpression subExp( subExpression );
715 QgsExpression filterExp( parameters.filter );
716
717 bool isStatic = true;
718 if ( filterExp.referencedVariables().contains( QStringLiteral( "parent" ) )
719 || filterExp.referencedVariables().contains( QString() )
720 || subExp.referencedVariables().contains( QStringLiteral( "parent" ) )
721 || subExp.referencedVariables().contains( QString() ) )
722 {
723 isStatic = false;
724 }
725 else
726 {
727
728 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
729 for ( const QString &varName : refVars )
730 {
731 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
732 if ( scope && !scope->isStatic( varName ) )
733 {
734 isStatic = false;
735 break;
736 }
737 }
738 }
739
740 if ( isStatic && ! parameters.orderBy.isEmpty() )
741 {
742 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
743 {
744 const QgsExpression &orderByExpression { orderByClause.expression() };
745 if ( orderByExpression.referencedVariables().contains( QStringLiteral( "parent" ) ) || orderByExpression.referencedVariables().contains( QString() ) )
746 {
747 isStatic = false;
748 break;
749 }
750 }
751 }
752
753 if ( !isStatic )
754 {
755 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
756 QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
757 }
758 else
759 {
760 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
761 }
762
763 if ( context->hasCachedValue( cacheKey ) )
764 {
765 return context->cachedValue( cacheKey );
766 }
767
768 QgsExpressionContext subContext( *context );
770 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
771 subContext.appendScope( subScope );
772 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
773
774 if ( ok )
775 {
776 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
777 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
778 // associated with it's calculation!
779 context->setCachedValue( cacheKey, result );
780 }
781 }
782 else
783 {
784 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
785 }
786 if ( !ok )
787 {
788 if ( !aggregateError.isEmpty() )
789 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
790 else
791 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
792 return QVariant();
793 }
794
795 return result;
796}
797
798static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
799{
800 if ( !context )
801 {
802 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
803 return QVariant();
804 }
805
806 // first step - find current layer
807
808 // TODO this expression function is NOT thread safe
810 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
812 if ( !vl )
813 {
814 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
815 return QVariant();
816 }
817
818 //lazy eval, so we need to evaluate nodes now
819
820 //first node is relation name
821 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
823 QVariant value = node->eval( parent, context );
825 QString relationId = value.toString();
826 // check relation exists
827 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId );
828 if ( !relation.isValid() || relation.referencedLayer() != vl )
829 {
830 // check for relations by name
831 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId );
832 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
833 {
834 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
835 return QVariant();
836 }
837 else
838 {
839 relation = relations.at( 0 );
840 }
841 }
842
843 QgsVectorLayer *childLayer = relation.referencingLayer();
844
845 // second node is aggregate type
846 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
848 value = node->eval( parent, context );
850 bool ok = false;
851 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
852 if ( !ok )
853 {
854 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
855 return QVariant();
856 }
857
858 //third node is subexpression (or field name)
859 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
861 QString subExpression = node->dump();
862
863 //optional fourth node is concatenator
865 if ( values.count() > 3 )
866 {
867 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
869 value = node->eval( parent, context );
871 parameters.delimiter = value.toString();
872 }
873
874 //optional fifth node is order by
875 QString orderBy;
876 if ( values.count() > 4 )
877 {
878 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
880 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
881 if ( !nl || nl->value().isValid() )
882 {
883 orderBy = node->dump();
884 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
885 }
886 }
887
888 if ( !context->hasFeature() )
889 return QVariant();
890 QgsFeature f = context->feature();
891
892 parameters.filter = relation.getRelatedFeaturesFilter( f );
893
894 QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4:%5" ).arg( vl->id(),
895 QString::number( static_cast< int >( aggregate ) ),
896 subExpression,
897 parameters.filter,
898 orderBy );
899 if ( context->hasCachedValue( cacheKey ) )
900 return context->cachedValue( cacheKey );
901
902 QVariant result;
903 ok = false;
904
905
906 QgsExpressionContext subContext( *context );
907 QString error;
908 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
909
910 if ( !ok )
911 {
912 if ( !error.isEmpty() )
913 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
914 else
915 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
916 return QVariant();
917 }
918
919 // cache value
920 context->setCachedValue( cacheKey, result );
921 return result;
922}
923
924
925static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
926{
927 if ( !context )
928 {
929 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
930 return QVariant();
931 }
932
933 // first step - find current layer
934
935 // TODO this expression function is NOT thread safe
937 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
939 if ( !vl )
940 {
941 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
942 return QVariant();
943 }
944
945 //lazy eval, so we need to evaluate nodes now
946
947 //first node is subexpression (or field name)
948 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
950 QString subExpression = node->dump();
951
952 //optional second node is group by
953 QString groupBy;
954 if ( values.count() > 1 )
955 {
956 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
958 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
959 if ( !nl || nl->value().isValid() )
960 groupBy = node->dump();
961 }
962
963 //optional third node is filter
964 if ( values.count() > 2 )
965 {
966 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
968 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
969 if ( !nl || nl->value().isValid() )
970 parameters.filter = node->dump();
971 }
972
973 //optional order by node, if supported
974 QString orderBy;
975 if ( orderByPos >= 0 && values.count() > orderByPos )
976 {
977 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
979 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
980 if ( !nl || nl->value().isValid() )
981 {
982 orderBy = node->dump();
983 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
984 }
985 }
986
987 // build up filter with group by
988
989 // find current group by value
990 if ( !groupBy.isEmpty() )
991 {
992 QgsExpression groupByExp( groupBy );
993 QVariant groupByValue = groupByExp.evaluate( context );
994 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
995 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
996 QgsExpression::quotedValue( groupByValue ) );
997 if ( !parameters.filter.isEmpty() )
998 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
999 else
1000 parameters.filter = groupByClause;
1001 }
1002
1003 QgsExpression subExp( subExpression );
1004 QgsExpression filterExp( parameters.filter );
1005
1006 bool isStatic = true;
1007 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1008 for ( const QString &varName : refVars )
1009 {
1010 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1011 if ( scope && !scope->isStatic( varName ) )
1012 {
1013 isStatic = false;
1014 break;
1015 }
1016 }
1017
1018 QString cacheKey;
1019 if ( !isStatic )
1020 {
1021 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1022 QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
1023 }
1024 else
1025 {
1026 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1027 }
1028
1029 if ( context->hasCachedValue( cacheKey ) )
1030 return context->cachedValue( cacheKey );
1031
1032 QVariant result;
1033 bool ok = false;
1034
1035 QgsExpressionContext subContext( *context );
1037 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
1038 subContext.appendScope( subScope );
1039 QString error;
1040 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1041
1042 if ( !ok )
1043 {
1044 if ( !error.isEmpty() )
1045 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1046 else
1047 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1048 return QVariant();
1049 }
1050
1051 // cache value
1052 context->setCachedValue( cacheKey, result );
1053 return result;
1054}
1055
1056
1057static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1058{
1059 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1060}
1061
1062static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1063{
1064 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1065}
1066
1067static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1068{
1069 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1070}
1071
1072static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1073{
1074 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1075}
1076
1077static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1078{
1079 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1080}
1081
1082static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1083{
1084 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1085}
1086
1087static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1088{
1089 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1090}
1091
1092static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1093{
1094 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1095}
1096
1097static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1098{
1099 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1100}
1101
1102static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1103{
1104 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1105}
1106
1107static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1108{
1109 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1110}
1111
1112static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1113{
1114 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1115}
1116
1117static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1118{
1119 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1120}
1121
1122static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1123{
1124 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1125}
1126
1127static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1128{
1129 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1130}
1131
1132static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1133{
1134 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1135}
1136
1137static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1138{
1139 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1140}
1141
1142static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1143{
1144 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1145}
1146
1147static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1148{
1150
1151 //fourth node is concatenator
1152 if ( values.count() > 3 )
1153 {
1154 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1156 QVariant value = node->eval( parent, context );
1158 parameters.delimiter = value.toString();
1159 }
1160
1161 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1162}
1163
1164static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1165{
1167
1168 //fourth node is concatenator
1169 if ( values.count() > 3 )
1170 {
1171 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1173 QVariant value = node->eval( parent, context );
1175 parameters.delimiter = value.toString();
1176 }
1177
1178 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1179}
1180
1181static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1182{
1183 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1184}
1185
1186static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1187{
1188 if ( !context )
1189 return QVariant();
1190
1191 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1192 bool ok = false;
1193 if ( QgsVariantUtils::isNull( scale ) )
1194 return QVariant();
1195
1196 const double v = scale.toDouble( &ok );
1197 if ( ok )
1198 return v;
1199 return QVariant();
1200}
1201
1202static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1203{
1204 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1205 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1206 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1207
1208 // force testValue to sit inside the range specified by the min and max value
1209 if ( testValue <= minValue )
1210 {
1211 return QVariant( minValue );
1212 }
1213 else if ( testValue >= maxValue )
1214 {
1215 return QVariant( maxValue );
1216 }
1217 else
1218 {
1219 return QVariant( testValue );
1220 }
1221}
1222
1223static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1224{
1225 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1226 return QVariant( std::floor( x ) );
1227}
1228
1229static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1230{
1231 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1232 return QVariant( std::ceil( x ) );
1233}
1234
1235static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1236{
1237 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1238}
1239static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1240{
1241 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1242}
1243static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1244{
1245 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1246}
1247
1248static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1249{
1250 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1251 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1252 if ( format.isEmpty() && !language.isEmpty() )
1253 {
1254 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1255 return QVariant( QDateTime() );
1256 }
1257
1258 if ( format.isEmpty() && language.isEmpty() )
1259 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1260
1261 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1262 QLocale locale = QLocale();
1263 if ( !language.isEmpty() )
1264 {
1265 locale = QLocale( language );
1266 }
1267
1268 QDateTime datetime = locale.toDateTime( datetimestring, format );
1269 if ( !datetime.isValid() )
1270 {
1271 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1272 datetime = QDateTime();
1273 }
1274 return QVariant( datetime );
1275}
1276
1277static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1278{
1279 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1280 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1281 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1282
1283 const QDate date( year, month, day );
1284 if ( !date.isValid() )
1285 {
1286 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1287 return QVariant();
1288 }
1289 return QVariant( date );
1290}
1291
1292static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1293{
1294 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1295 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1296 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1297
1298 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1299 if ( !time.isValid() )
1300 {
1301 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1302 return QVariant();
1303 }
1304 return QVariant( time );
1305}
1306
1307static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1308{
1309 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1310 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1311 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1312 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1313 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1314 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1315
1316 const QDate date( year, month, day );
1317 if ( !date.isValid() )
1318 {
1319 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1320 return QVariant();
1321 }
1322 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1323 if ( !time.isValid() )
1324 {
1325 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1326 return QVariant();
1327 }
1328 return QVariant( QDateTime( date, time ) );
1329}
1330
1331static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1332{
1333 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1334 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1335 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1336 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1337 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1338 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1339 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1340
1341 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1342}
1343
1344static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1345{
1346 for ( const QVariant &value : values )
1347 {
1348 if ( QgsVariantUtils::isNull( value ) )
1349 continue;
1350 return value;
1351 }
1352 return QVariant();
1353}
1354
1355static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1356{
1357 const QVariant val1 = values.at( 0 );
1358 const QVariant val2 = values.at( 1 );
1359
1360 if ( val1 == val2 )
1361 return QVariant();
1362 else
1363 return val1;
1364}
1365
1366static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1367{
1368 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1369 return QVariant( str.toLower() );
1370}
1371static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1372{
1373 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1374 return QVariant( str.toUpper() );
1375}
1376static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1377{
1378 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1379 QStringList elems = str.split( ' ' );
1380 for ( int i = 0; i < elems.size(); i++ )
1381 {
1382 if ( elems[i].size() > 1 )
1383 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1384 }
1385 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1386}
1387
1388static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1389{
1390 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1391 return QVariant( str.trimmed() );
1392}
1393
1394static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1395{
1396 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1397
1398 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1399
1400 const QRegularExpression re( QStringLiteral( "^([%1]*)" ).arg( QRegularExpression::escape( characters ) ) );
1401 str.replace( re, QString() );
1402 return QVariant( str );
1403}
1404
1405static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1406{
1407 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1408
1409 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1410
1411 const QRegularExpression re( QStringLiteral( "([%1]*)$" ).arg( QRegularExpression::escape( characters ) ) );
1412 str.replace( re, QString() );
1413 return QVariant( str );
1414}
1415
1416static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1417{
1418 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1419 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1420 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1421}
1422
1423static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1424{
1425 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1426 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1427 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1428}
1429
1430static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1431{
1432 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1433 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1434 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1435 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1436}
1437
1438static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1439{
1440 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1441 return QVariant( QgsStringUtils::soundex( string ) );
1442}
1443
1444static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1445{
1446 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1447 return QVariant( QString( character ) );
1448}
1449
1450static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1451{
1452 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1453
1454 if ( value.isEmpty() )
1455 {
1456 return QVariant();
1457 }
1458
1459 int res = value.at( 0 ).unicode();
1460 return QVariant( res );
1461}
1462
1463static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1464{
1465 if ( values.length() == 2 || values.length() == 3 )
1466 {
1467 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1468 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1469
1470 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1471
1472 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1473 }
1474
1475 return QVariant();
1476}
1477
1478static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1479{
1480 // two variants, one for geometry, one for string
1481 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGeometry>() )
1482 {
1483 //geometry variant
1484 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1485 if ( geom.type() != Qgis::GeometryType::Line )
1486 return QVariant();
1487
1488 return QVariant( geom.length() );
1489 }
1490
1491 //otherwise fall back to string variant
1492 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1493 return QVariant( str.length() );
1494}
1495
1496static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1497{
1498 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1499
1500 if ( geom.type() != Qgis::GeometryType::Line )
1501 return QVariant();
1502
1503 double totalLength = 0;
1504 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1505 {
1506 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1507 {
1508 totalLength += line->length3D();
1509 }
1510 else
1511 {
1512 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1513 totalLength += segmentized->length3D();
1514 }
1515 }
1516
1517 return totalLength;
1518}
1519
1520static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1521{
1522 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1523 {
1524 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1525 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1526 QVector< QPair< QString, QString > > mapItems;
1527
1528 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1529 {
1530 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1531 }
1532
1533 // larger keys should be replaced first since they may contain whole smaller keys
1534 std::sort( mapItems.begin(),
1535 mapItems.end(),
1536 []( const QPair< QString, QString > &pair1,
1537 const QPair< QString, QString > &pair2 )
1538 {
1539 return ( pair1.first.length() > pair2.first.length() );
1540 } );
1541
1542 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1543 {
1544 str = str.replace( it->first, it->second );
1545 }
1546
1547 return QVariant( str );
1548 }
1549 else if ( values.count() == 3 )
1550 {
1551 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1552 QVariantList before;
1553 QVariantList after;
1554 bool isSingleReplacement = false;
1555
1556 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1557 {
1558 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1559 }
1560 else
1561 {
1562 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1563 }
1564
1565 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1566 {
1567 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1568 isSingleReplacement = true;
1569 }
1570 else
1571 {
1572 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1573 }
1574
1575 if ( !isSingleReplacement && before.length() != after.length() )
1576 {
1577 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1578 return QVariant();
1579 }
1580
1581 for ( int i = 0; i < before.length(); i++ )
1582 {
1583 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1584 }
1585
1586 return QVariant( str );
1587 }
1588 else
1589 {
1590 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1591 return QVariant();
1592 }
1593}
1594
1595static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1596{
1597 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1598 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1599 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1600
1601 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1602 if ( !re.isValid() )
1603 {
1604 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1605 return QVariant();
1606 }
1607 return QVariant( str.replace( re, after ) );
1608}
1609
1610static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1611{
1612 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1613 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1614
1615 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1616 if ( !re.isValid() )
1617 {
1618 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1619 return QVariant();
1620 }
1621 return QVariant( ( str.indexOf( re ) + 1 ) );
1622}
1623
1624static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1625{
1626 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1627 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1628 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1629
1630 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1631 if ( !re.isValid() )
1632 {
1633 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1634 return QVariant();
1635 }
1636
1637 QRegularExpressionMatch matches = re.match( str );
1638 if ( matches.hasMatch() )
1639 {
1640 QVariantList array;
1641 QStringList list = matches.capturedTexts();
1642
1643 // Skip the first string to only return captured groups
1644 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1645 {
1646 array += ( !( *it ).isEmpty() ) ? *it : empty;
1647 }
1648
1649 return QVariant( array );
1650 }
1651 else
1652 {
1653 return QVariant();
1654 }
1655}
1656
1657static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1658{
1659 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1660 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1661
1662 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1663 if ( !re.isValid() )
1664 {
1665 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1666 return QVariant();
1667 }
1668
1669 // extract substring
1670 QRegularExpressionMatch match = re.match( str );
1671 if ( match.hasMatch() )
1672 {
1673 // return first capture
1674 if ( match.lastCapturedIndex() > 0 )
1675 {
1676 // a capture group was present, so use that
1677 return QVariant( match.captured( 1 ) );
1678 }
1679 else
1680 {
1681 // no capture group, so using all match
1682 return QVariant( match.captured( 0 ) );
1683 }
1684 }
1685 else
1686 {
1687 return QVariant( "" );
1688 }
1689}
1690
1691static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1692{
1693 QString uuid = QUuid::createUuid().toString();
1694 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1695 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1696 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1697 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1698 return uuid;
1699}
1700
1701static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1702{
1703 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1704 return QVariant();
1705
1706 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1707 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1708
1709 int len = 0;
1710 if ( values.at( 2 ).isValid() )
1711 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1712 else
1713 len = str.size();
1714
1715 if ( from < 0 )
1716 {
1717 from = str.size() + from;
1718 if ( from < 0 )
1719 {
1720 from = 0;
1721 }
1722 }
1723 else if ( from > 0 )
1724 {
1725 //account for the fact that substr() starts at 1
1726 from -= 1;
1727 }
1728
1729 if ( len < 0 )
1730 {
1731 len = str.size() + len - from;
1732 if ( len < 0 )
1733 {
1734 len = 0;
1735 }
1736 }
1737
1738 return QVariant( str.mid( from, len ) );
1739}
1740static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1741{
1742 FEAT_FROM_CONTEXT( context, f )
1743 // TODO: handling of 64-bit feature ids?
1744 return QVariant( static_cast< int >( f.id() ) );
1745}
1746
1747static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1748{
1749 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1750 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1751 bool foundLayer = false;
1752 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1753 {
1754 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1755 if ( !layer || !layer->dataProvider() )
1756 {
1757 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1758 return QVariant();
1759 }
1760
1761 if ( bandNb < 1 || bandNb > layer->bandCount() )
1762 {
1763 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1764 return QVariant();
1765 }
1766
1767 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1768 {
1769 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1770 return QVariant();
1771 }
1772
1773 QgsPointXY point = geom.asPoint();
1774 if ( geom.isMultipart() )
1775 {
1776 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1777 if ( multiPoint.count() == 1 )
1778 {
1779 point = multiPoint[0];
1780 }
1781 else
1782 {
1783 // if the geometry contains more than one part, return an undefined value
1784 return QVariant();
1785 }
1786 }
1787
1788 double value = layer->dataProvider()->sample( point, bandNb );
1789 return std::isnan( value ) ? QVariant() : value;
1790 },
1791 foundLayer );
1792
1793 if ( !foundLayer )
1794 {
1795 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1796 return QVariant();
1797 }
1798 else
1799 {
1800 return res;
1801 }
1802}
1803
1804static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1805{
1806 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1807 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1808
1809 bool foundLayer = false;
1810 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1811 {
1812 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1813 if ( !layer || !layer->dataProvider() )
1814 {
1815 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1816 return QVariant();
1817 }
1818
1819 if ( bandNb < 1 || bandNb > layer->bandCount() )
1820 {
1821 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1822 return QVariant();
1823 }
1824
1825 if ( std::isnan( value ) )
1826 {
1827 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1828 return QVariant();
1829 }
1830
1831 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1832 {
1833 return QVariant();
1834 }
1835
1836 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1837 if ( data.isEmpty() )
1838 {
1839 return QVariant();
1840 }
1841
1842 QVariantMap result;
1843 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1844 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1845 {
1846 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1847 if ( field.isColor() || field.isRamp() )
1848 {
1849 continue;
1850 }
1851 result.insert( fields.at( idx ).name, data.at( idx ) );
1852 }
1853
1854 return result;
1855 }, foundLayer );
1856
1857 if ( !foundLayer )
1858 {
1859 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1860 return QVariant();
1861 }
1862 else
1863 {
1864 return res;
1865 }
1866}
1867
1868static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1869{
1870 if ( !context )
1871 return QVariant();
1872
1873 return context->feature();
1874}
1875
1876static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1877{
1878 QgsFeature feature;
1879 QString attr;
1880 if ( values.size() == 1 )
1881 {
1882 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1883 feature = context->feature();
1884 }
1885 else if ( values.size() == 2 )
1886 {
1887 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1888 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1889 }
1890 else
1891 {
1892 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1893 return QVariant();
1894 }
1895
1896 return feature.attribute( attr );
1897}
1898
1899static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1900{
1901 QString table { R"html(
1902 <table>
1903 <thead>
1904 <tr><th>%1</th></tr>
1905 </thead>
1906 <tbody>
1907 <tr><td>%2</td></tr>
1908 </tbody>
1909 </table>)html" };
1910 QVariantMap dict;
1911 if ( values.size() == 1 )
1912 {
1913 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1914 }
1915 else
1916 {
1917 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1918 return QVariant();
1919 }
1920
1921 if ( dict.isEmpty() )
1922 {
1923 return QVariant();
1924 }
1925
1926 QStringList headers;
1927 QStringList cells;
1928
1929 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1930 {
1931 headers.push_back( it.key().toHtmlEscaped() );
1932 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1933 }
1934
1935 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1936}
1937
1938static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1939{
1940 QString table { R"html(
1941 <dl>
1942 %1
1943 </dl>)html" };
1944 QVariantMap dict;
1945 if ( values.size() == 1 )
1946 {
1947 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1948 }
1949 else
1950 {
1951 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1952 return QVariant();
1953 }
1954
1955 if ( dict.isEmpty() )
1956 {
1957 return QVariant();
1958 }
1959
1960 QString rows;
1961
1962 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1963 {
1964 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
1965 }
1966
1967 return table.arg( rows );
1968}
1969
1970static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1971{
1972 QVariant layer;
1973 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
1974 {
1975 layer = context->variable( QStringLiteral( "layer" ) );
1976 }
1977 else
1978 {
1979 //first node is layer id or name
1980 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1982 layer = node->eval( parent, context );
1984 }
1985
1986 QgsFeature feature;
1987 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
1988 {
1989 feature = context->feature();
1990 }
1991 else
1992 {
1993 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1994 }
1995
1997 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
1998 if ( strength == QLatin1String( "hard" ) )
1999 {
2001 }
2002 else if ( strength == QLatin1String( "soft" ) )
2003 {
2005 }
2006
2007 bool foundLayer = false;
2008 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2009 {
2010 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2011 if ( !layer )
2012 {
2013 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2014 return QVariant();
2015 }
2016
2017 const QgsFields fields = layer->fields();
2018 bool valid = true;
2019 for ( int i = 0; i < fields.size(); i++ )
2020 {
2021 QStringList errors;
2022 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2023 if ( !valid )
2024 {
2025 break;
2026 }
2027 }
2028
2029 return valid;
2030 }, foundLayer );
2031
2032 if ( !foundLayer )
2033 {
2034 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2035 return QVariant();
2036 }
2037
2038 return res;
2039}
2040
2041static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2042{
2043 QVariant layer;
2044 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2045 {
2046 layer = context->variable( QStringLiteral( "layer" ) );
2047 }
2048 else
2049 {
2050 //first node is layer id or name
2051 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2053 layer = node->eval( parent, context );
2055 }
2056
2057 QgsFeature feature;
2058 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2059 {
2060 feature = context->feature();
2061 }
2062 else
2063 {
2064 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2065 }
2066
2068 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2069 if ( strength == QLatin1String( "hard" ) )
2070 {
2072 }
2073 else if ( strength == QLatin1String( "soft" ) )
2074 {
2076 }
2077
2078 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2079
2080 bool foundLayer = false;
2081 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2082 {
2083 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2084 if ( !layer )
2085 {
2086 return QVariant();
2087 }
2088
2089 const int fieldIndex = layer->fields().indexFromName( attributeName );
2090 if ( fieldIndex == -1 )
2091 {
2092 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2093 return QVariant();
2094 }
2095
2096 QStringList errors;
2097 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2098 return valid;
2099 }, foundLayer );
2100
2101 if ( !foundLayer )
2102 {
2103 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2104 return QVariant();
2105 }
2106
2107 return res;
2108}
2109
2110static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2111{
2112 QgsFeature feature;
2113 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2114 {
2115 feature = context->feature();
2116 }
2117 else
2118 {
2119 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2120 }
2121
2122 const QgsFields fields = feature.fields();
2123 QVariantMap result;
2124 for ( int i = 0; i < fields.count(); ++i )
2125 {
2126 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2127 }
2128 return result;
2129}
2130
2131static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2132{
2133 QgsVectorLayer *layer = nullptr;
2134 QgsFeature feature;
2135
2136 // TODO this expression function is NOT thread safe
2138 if ( values.isEmpty() )
2139 {
2140 feature = context->feature();
2141 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2142 }
2143 else if ( values.size() == 1 )
2144 {
2145 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2146 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2147 }
2148 else if ( values.size() == 2 )
2149 {
2150 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2151 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2152 }
2153 else
2154 {
2155 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2156 return QVariant();
2157 }
2159
2160 if ( !layer )
2161 {
2162 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2163 return QVariant();
2164 }
2165
2166 if ( !feature.isValid() )
2167 {
2168 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2169 return QVariant();
2170 }
2171
2172 const QgsFields fields = feature.fields();
2173 QVariantMap result;
2174 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2175 {
2176 const QString fieldName { fields.at( fieldIndex ).name() };
2177 const QVariant attributeVal = feature.attribute( fieldIndex );
2178 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2179 if ( context && context->hasCachedValue( cacheValueKey ) )
2180 {
2181 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2182 }
2183 else
2184 {
2185 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2187 QVariant cache;
2188 if ( context )
2189 {
2190 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2191
2192 if ( !context->hasCachedValue( cacheKey ) )
2193 {
2194 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2195 context->setCachedValue( cacheKey, cache );
2196 }
2197 else
2198 {
2199 cache = context->cachedValue( cacheKey );
2200 }
2201 }
2202 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2203
2204 result.insert( fields.at( fieldIndex ).name(), value );
2205
2206 if ( context )
2207 {
2208 context->setCachedValue( cacheValueKey, value );
2209 }
2210
2211 }
2212 }
2213 return result;
2214}
2215
2216static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2217{
2218 QgsVectorLayer *layer = nullptr;
2219 QgsFeature feature;
2220 bool evaluate = true;
2221
2222 // TODO this expression function is NOT thread safe
2224 if ( values.isEmpty() )
2225 {
2226 feature = context->feature();
2227 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2228 }
2229 else if ( values.size() == 1 )
2230 {
2231 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2232 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2233 }
2234 else if ( values.size() == 2 )
2235 {
2236 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2237 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2238 }
2239 else if ( values.size() == 3 )
2240 {
2241 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2242 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2243 evaluate = values.value( 2 ).toBool();
2244 }
2245 else
2246 {
2247 if ( isMaptip )
2248 {
2249 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2250 }
2251 else
2252 {
2253 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2254 }
2255 return QVariant();
2256 }
2257
2258 if ( !layer )
2259 {
2260 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2261 return QVariant( );
2262 }
2264
2265 if ( !feature.isValid() )
2266 {
2267 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2268 return QVariant( );
2269 }
2270
2271 if ( ! evaluate )
2272 {
2273 if ( isMaptip )
2274 {
2275 return layer->mapTipTemplate();
2276 }
2277 else
2278 {
2279 return layer->displayExpression();
2280 }
2281 }
2282
2283 QgsExpressionContext subContext( *context );
2284 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2285 subContext.setFeature( feature );
2286
2287 if ( isMaptip )
2288 {
2289 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2290 }
2291 else
2292 {
2293 QgsExpression exp( layer->displayExpression() );
2294 exp.prepare( &subContext );
2295 return exp.evaluate( &subContext ).toString();
2296 }
2297}
2298
2299static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2300{
2301 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2302}
2303
2304static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2305{
2306 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2307}
2308
2309static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2310{
2311 QgsFeature feature;
2312 QVariant layer;
2313 if ( values.isEmpty() )
2314 {
2315 feature = context->feature();
2316 layer = context->variable( QStringLiteral( "layer" ) );
2317 }
2318 else if ( values.size() == 1 )
2319 {
2320 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2321 layer = context->variable( QStringLiteral( "layer" ) );
2322 }
2323 else if ( values.size() == 2 )
2324 {
2325 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2326 layer = values.at( 0 );
2327 }
2328 else
2329 {
2330 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2331 return QVariant();
2332 }
2333
2334 bool foundLayer = false;
2335 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2336 {
2337 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2338 if ( !layer || !feature.isValid() )
2339 {
2340 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2341 }
2342
2343 return layer->selectedFeatureIds().contains( feature.id() );
2344 }, foundLayer );
2345 if ( !foundLayer )
2346 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2347 else
2348 return res;
2349}
2350
2351static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2352{
2353 QVariant layer;
2354
2355 if ( values.isEmpty() )
2356 layer = context->variable( QStringLiteral( "layer" ) );
2357 else if ( values.count() == 1 )
2358 layer = values.at( 0 );
2359 else
2360 {
2361 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2362 return QVariant();
2363 }
2364
2365 bool foundLayer = false;
2366 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2367 {
2368 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2369 if ( !layer )
2370 {
2371 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2372 }
2373
2374 return layer->selectedFeatureCount();
2375 }, foundLayer );
2376 if ( !foundLayer )
2377 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2378 else
2379 return res;
2380}
2381
2382static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2383{
2384 static QMap<QString, qlonglong> counterCache;
2385 QVariant functionResult;
2386
2387 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2388 {
2389 QString database;
2390
2391 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2392
2393 if ( layer )
2394 {
2395 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2396 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2397 if ( database.isEmpty() )
2398 {
2399 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2400 }
2401 }
2402 else
2403 {
2404 database = databaseArgument;
2405 }
2406
2407 const QString table = values.at( 1 ).toString();
2408 const QString idColumn = values.at( 2 ).toString();
2409 const QString filterAttribute = values.at( 3 ).toString();
2410 const QVariant filterValue = values.at( 4 ).toString();
2411 const QVariantMap defaultValues = values.at( 5 ).toMap();
2412
2413 // read from database
2415 sqlite3_statement_unique_ptr sqliteStatement;
2416
2417 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2418 {
2419 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2420 functionResult = QVariant();
2421 return;
2422 }
2423
2424 QString errorMessage;
2425 QString currentValSql;
2426
2427 qlonglong nextId = 0;
2428 bool cachedMode = false;
2429 bool valueRetrieved = false;
2430
2431 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2432
2433 // Running in transaction mode, check for cached value first
2434 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2435 {
2436 cachedMode = true;
2437
2438 auto cachedCounter = counterCache.find( cacheString );
2439
2440 if ( cachedCounter != counterCache.end() )
2441 {
2442 qlonglong &cachedValue = cachedCounter.value();
2443 nextId = cachedValue;
2444 nextId += 1;
2445 cachedValue = nextId;
2446 valueRetrieved = true;
2447 }
2448 }
2449
2450 // Either not in cached mode or no cached value found, obtain from DB
2451 if ( !cachedMode || !valueRetrieved )
2452 {
2453 int result = SQLITE_ERROR;
2454
2455 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2456 if ( !filterAttribute.isNull() )
2457 {
2458 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2459 }
2460
2461 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2462
2463 if ( result == SQLITE_OK )
2464 {
2465 nextId = 0;
2466 if ( sqliteStatement.step() == SQLITE_ROW )
2467 {
2468 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2469 }
2470
2471 // If in cached mode: add value to cache and connect to transaction
2472 if ( cachedMode && result == SQLITE_OK )
2473 {
2474 counterCache.insert( cacheString, nextId );
2475
2476 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2477 {
2478 counterCache.remove( cacheString );
2479 } );
2480 }
2481 valueRetrieved = true;
2482 }
2483 }
2484
2485 if ( valueRetrieved )
2486 {
2487 QString upsertSql;
2488 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2489 QStringList cols;
2490 QStringList vals;
2491 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2492 vals << QgsSqliteUtils::quotedValue( nextId );
2493
2494 if ( !filterAttribute.isNull() )
2495 {
2496 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2497 vals << QgsSqliteUtils::quotedValue( filterValue );
2498 }
2499
2500 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2501 {
2502 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2503 vals << iter.value().toString();
2504 }
2505
2506 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2507 upsertSql += QLatin1String( " VALUES " );
2508 upsertSql += '(' + vals.join( ',' ) + ')';
2509
2510 int result = SQLITE_ERROR;
2511 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2512 {
2513 QgsTransaction *transaction = layer->dataProvider()->transaction();
2514 if ( transaction->executeSql( upsertSql, errorMessage ) )
2515 {
2516 result = SQLITE_OK;
2517 }
2518 }
2519 else
2520 {
2521 result = sqliteDb.exec( upsertSql, errorMessage );
2522 }
2523 if ( result == SQLITE_OK )
2524 {
2525 functionResult = QVariant( nextId );
2526 return;
2527 }
2528 else
2529 {
2530 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2531 functionResult = QVariant();
2532 return;
2533 }
2534 }
2535
2536 functionResult = QVariant();
2537 };
2538
2539 bool foundLayer = false;
2540 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2541 {
2542 fetchAndIncrementFunc( layer, QString() );
2543 }, foundLayer );
2544 if ( !foundLayer )
2545 {
2546 const QString databasePath = values.at( 0 ).toString();
2547 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2548 {
2549 fetchAndIncrementFunc( nullptr, databasePath );
2550 } );
2551 }
2552
2553 return functionResult;
2554}
2555
2556static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2557{
2558 QString concat;
2559 for ( const QVariant &value : values )
2560 {
2561 if ( !QgsVariantUtils::isNull( value ) )
2562 concat += QgsExpressionUtils::getStringValue( value, parent );
2563 }
2564 return concat;
2565}
2566
2567static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2568{
2569 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2570 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2571}
2572
2573static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2574{
2575 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2576 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2577 return string.right( pos );
2578}
2579
2580static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2581{
2582 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2583 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2584 return string.left( pos );
2585}
2586
2587static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2588{
2589 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2590 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2591 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2592 return string.leftJustified( length, fill.at( 0 ), true );
2593}
2594
2595static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2596{
2597 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2598 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2599 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2600 return string.rightJustified( length, fill.at( 0 ), true );
2601}
2602
2603static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2604{
2605 if ( values.size() < 1 )
2606 {
2607 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2608 return QVariant();
2609 }
2610
2611 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2612 for ( int n = 1; n < values.length(); n++ )
2613 {
2614 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2615 }
2616 return string;
2617}
2618
2619
2620static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2621{
2622 return QVariant( QDateTime::currentDateTime() );
2623}
2624
2625static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2626{
2627 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2628 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2629 if ( format.isEmpty() && !language.isEmpty() )
2630 {
2631 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2632 return QVariant( QDate() );
2633 }
2634
2635 if ( format.isEmpty() && language.isEmpty() )
2636 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2637
2638 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2639 QLocale locale = QLocale();
2640 if ( !language.isEmpty() )
2641 {
2642 locale = QLocale( language );
2643 }
2644
2645 QDate date = locale.toDate( datestring, format );
2646 if ( !date.isValid() )
2647 {
2648 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2649 date = QDate();
2650 }
2651 return QVariant( date );
2652}
2653
2654static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2655{
2656 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2657 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2658 if ( format.isEmpty() && !language.isEmpty() )
2659 {
2660 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2661 return QVariant( QTime() );
2662 }
2663
2664 if ( format.isEmpty() && language.isEmpty() )
2665 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2666
2667 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2668 QLocale locale = QLocale();
2669 if ( !language.isEmpty() )
2670 {
2671 locale = QLocale( language );
2672 }
2673
2674 QTime time = locale.toTime( timestring, format );
2675 if ( !time.isValid() )
2676 {
2677 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2678 time = QTime();
2679 }
2680 return QVariant( time );
2681}
2682
2683static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2684{
2685 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2686}
2687
2688/*
2689 * DMS functions
2690 */
2691
2692static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2693{
2694 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2695 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2696 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2697
2698 QString formatString;
2699 if ( values.count() > 3 )
2700 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2701
2703 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2704 {
2706 }
2707 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2708 {
2710 }
2711 else if ( ! formatString.isEmpty() )
2712 {
2713 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2714 return QVariant();
2715 }
2716
2717 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2718 {
2719 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2720 }
2721 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2722 {
2723 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2724 }
2725 else
2726 {
2727 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2728 return QVariant();
2729 }
2730}
2731
2732static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2733{
2735 return floatToDegreeFormat( format, values, context, parent, node );
2736}
2737
2738static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2739{
2740 double value = 0.0;
2741 bool ok = false;
2742 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2743
2744 return ok ? QVariant( value ) : QVariant();
2745}
2746
2747static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2748{
2750 return floatToDegreeFormat( format, values, context, parent, node );
2751}
2752
2753static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2754{
2755 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2756 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2757 qint64 seconds = d2.secsTo( d1 );
2758 return QVariant::fromValue( QgsInterval( seconds ) );
2759}
2760
2761static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2762{
2763 if ( !values.at( 0 ).canConvert<QDate>() )
2764 return QVariant();
2765
2766 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2767 if ( !date.isValid() )
2768 return QVariant();
2769
2770 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2771 // (to match PostgreSQL behavior)
2772 return date.dayOfWeek() % 7;
2773}
2774
2775static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2776{
2777 QVariant value = values.at( 0 );
2778 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2779 if ( inter.isValid() )
2780 {
2781 return QVariant( inter.days() );
2782 }
2783 else
2784 {
2785 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2786 return QVariant( d1.date().day() );
2787 }
2788}
2789
2790static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2791{
2792 QVariant value = values.at( 0 );
2793 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2794 if ( inter.isValid() )
2795 {
2796 return QVariant( inter.years() );
2797 }
2798 else
2799 {
2800 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2801 return QVariant( d1.date().year() );
2802 }
2803}
2804
2805static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2806{
2807 QVariant value = values.at( 0 );
2808 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2809 if ( inter.isValid() )
2810 {
2811 return QVariant( inter.months() );
2812 }
2813 else
2814 {
2815 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2816 return QVariant( d1.date().month() );
2817 }
2818}
2819
2820static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2821{
2822 QVariant value = values.at( 0 );
2823 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2824 if ( inter.isValid() )
2825 {
2826 return QVariant( inter.weeks() );
2827 }
2828 else
2829 {
2830 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2831 return QVariant( d1.date().weekNumber() );
2832 }
2833}
2834
2835static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2836{
2837 QVariant value = values.at( 0 );
2838 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2839 if ( inter.isValid() )
2840 {
2841 return QVariant( inter.hours() );
2842 }
2843 else
2844 {
2845 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2846 return QVariant( t1.hour() );
2847 }
2848}
2849
2850static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2851{
2852 QVariant value = values.at( 0 );
2853 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2854 if ( inter.isValid() )
2855 {
2856 return QVariant( inter.minutes() );
2857 }
2858 else
2859 {
2860 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2861 return QVariant( t1.minute() );
2862 }
2863}
2864
2865static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2866{
2867 QVariant value = values.at( 0 );
2868 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2869 if ( inter.isValid() )
2870 {
2871 return QVariant( inter.seconds() );
2872 }
2873 else
2874 {
2875 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2876 return QVariant( t1.second() );
2877 }
2878}
2879
2880static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2881{
2882 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2883 if ( dt.isValid() )
2884 {
2885 return QVariant( dt.toMSecsSinceEpoch() );
2886 }
2887 else
2888 {
2889 return QVariant();
2890 }
2891}
2892
2893static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2894{
2895 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2896 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2897 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2898}
2899
2900static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2901{
2902 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2903 if ( parent->hasEvalError() )
2904 {
2905 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2906 return QVariant();
2907 }
2908 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2909 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2910}
2911
2912static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2914 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2915 if ( parent->hasEvalError() )
2916 {
2917 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2918 return QVariant();
2919 }
2920 bool ok;
2921 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2922}
2923
2924#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2925 if ( !(f).hasGeometry() ) \
2926 return QVariant(); \
2927 QgsGeometry g = (f).geometry(); \
2928 if ( (g).type() != (geomtype) ) \
2929 return QVariant();
2930
2931static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2932{
2933 FEAT_FROM_CONTEXT( context, f )
2935 if ( g.isMultipart() )
2936 {
2937 return g.asMultiPoint().at( 0 ).x();
2938 }
2939 else
2940 {
2941 return g.asPoint().x();
2942 }
2943}
2944
2945static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2946{
2947 FEAT_FROM_CONTEXT( context, f )
2949 if ( g.isMultipart() )
2950 {
2951 return g.asMultiPoint().at( 0 ).y();
2952 }
2953 else
2954 {
2955 return g.asPoint().y();
2956 }
2957}
2958
2959static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2960{
2961 FEAT_FROM_CONTEXT( context, f )
2963
2964 if ( g.isEmpty() )
2965 return QVariant();
2966
2967 const QgsAbstractGeometry *abGeom = g.constGet();
2968
2969 if ( g.isEmpty() || !abGeom->is3D() )
2970 return QVariant();
2971
2972 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
2973 {
2974 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
2975 if ( point )
2976 return point->z();
2977 }
2978 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
2979 {
2980 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
2981 {
2982 if ( collection->numGeometries() > 0 )
2983 {
2984 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2985 return point->z();
2986 }
2987 }
2988 }
2989
2990 return QVariant();
2991}
2992
2993static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2994{
2995 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2996 if ( geom.isNull() )
2997 return QVariant();
2998
2999 bool isValid = geom.isGeosValid();
3000
3001 return QVariant( isValid );
3002}
3003
3004static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3005{
3006 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3007 if ( geom.isNull() )
3008 return QVariant();
3009
3010 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3011#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3013#else
3015#endif
3016 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3018 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3020
3021 const bool keepCollapsed = values.value( 2 ).toBool();
3022
3023 QgsGeometry valid;
3024 try
3025 {
3026 valid = geom.makeValid( method, keepCollapsed );
3027 }
3028 catch ( QgsNotSupportedException & )
3029 {
3030 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3031 return QVariant();
3032 }
3033
3034 return QVariant::fromValue( valid );
3035}
3036
3037static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3038{
3039 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3040 if ( geom.isNull() )
3041 return QVariant();
3042
3043 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3044 QVariantList array;
3045 for ( int i = 0; i < multiGeom.size(); ++i )
3046 {
3047 array += QVariant::fromValue( multiGeom.at( i ) );
3048 }
3049
3050 return array;
3051}
3052
3053static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3054{
3055 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3056 if ( geom.isNull() )
3057 return QVariant();
3058
3059 //if single point, return the point's x coordinate
3060 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3061 {
3062 return geom.asPoint().x();
3063 }
3064
3065 //otherwise return centroid x
3066 QgsGeometry centroid = geom.centroid();
3067 QVariant result( centroid.asPoint().x() );
3068 return result;
3069}
3070
3071static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3072{
3073 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3074 if ( geom.isNull() )
3075 return QVariant();
3076
3077 //if single point, return the point's y coordinate
3078 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3079 {
3080 return geom.asPoint().y();
3081 }
3082
3083 //otherwise return centroid y
3084 QgsGeometry centroid = geom.centroid();
3085 QVariant result( centroid.asPoint().y() );
3086 return result;
3087}
3088
3089static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3090{
3091 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3092 if ( geom.isNull() )
3093 return QVariant(); //or 0?
3094
3095 if ( !geom.constGet()->is3D() )
3096 return QVariant();
3097
3098 //if single point, return the point's z coordinate
3099 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3100 {
3101 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3102 if ( point )
3103 return point->z();
3104 }
3105 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3106 {
3107 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3108 {
3109 if ( collection->numGeometries() == 1 )
3110 {
3111 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3112 return point->z();
3113 }
3114 }
3115 }
3116
3117 return QVariant();
3118}
3119
3120static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3121{
3122 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3123 if ( geom.isNull() )
3124 return QVariant(); //or 0?
3125
3126 if ( !geom.constGet()->isMeasure() )
3127 return QVariant();
3128
3129 //if single point, return the point's m value
3130 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3131 {
3132 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3133 if ( point )
3134 return point->m();
3135 }
3136 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3137 {
3138 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3139 {
3140 if ( collection->numGeometries() == 1 )
3141 {
3142 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3143 return point->m();
3144 }
3145 }
3146 }
3147
3148 return QVariant();
3149}
3150
3151static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3152{
3153 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3154
3155 if ( geom.isNull() )
3156 return QVariant();
3157
3158 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3159
3160 if ( idx < 0 )
3161 {
3162 //negative idx
3163 int count = geom.constGet()->nCoordinates();
3164 idx = count + idx;
3165 }
3166 else
3167 {
3168 //positive idx is 1 based
3169 idx -= 1;
3170 }
3171
3172 QgsVertexId vId;
3173 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3174 {
3175 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3176 return QVariant();
3177 }
3178
3179 QgsPoint point = geom.constGet()->vertexAt( vId );
3180 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3181}
3182
3183static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3184{
3185 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3186
3187 if ( geom.isNull() )
3188 return QVariant();
3189
3190 QgsVertexId vId;
3191 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3192 {
3193 return QVariant();
3194 }
3195
3196 QgsPoint point = geom.constGet()->vertexAt( vId );
3197 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3198}
3199
3200static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3201{
3202 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3203
3204 if ( geom.isNull() )
3205 return QVariant();
3206
3207 QgsVertexId vId;
3208 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3209 {
3210 return QVariant();
3211 }
3212
3213 QgsPoint point = geom.constGet()->vertexAt( vId );
3214 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3215}
3216
3217static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3218{
3219 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3220
3221 if ( geom.isNull() )
3222 return QVariant();
3223
3224 bool ignoreClosing = false;
3225 if ( values.length() > 1 )
3226 {
3227 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3228 }
3229
3230 QgsMultiPoint *mp = new QgsMultiPoint();
3231
3232 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3233 for ( const QgsRingSequence &part : sequence )
3234 {
3235 for ( const QgsPointSequence &ring : part )
3236 {
3237 bool skipLast = false;
3238 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3239 {
3240 skipLast = true;
3241 }
3242
3243 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3244 {
3245 mp->addGeometry( ring.at( i ).clone() );
3246 }
3247 }
3248 }
3249
3250 return QVariant::fromValue( QgsGeometry( mp ) );
3251}
3252
3253static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3254{
3255 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3256
3257 if ( geom.isNull() )
3258 return QVariant();
3259
3260 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3261
3262 //OK, now we have a complete list of segmentized lines from the geometry
3264 for ( QgsLineString *line : linesToProcess )
3265 {
3266 for ( int i = 0; i < line->numPoints() - 1; ++i )
3267 {
3269 segment->setPoints( QgsPointSequence()
3270 << line->pointN( i )
3271 << line->pointN( i + 1 ) );
3272 ml->addGeometry( segment );
3273 }
3274 delete line;
3275 }
3276
3277 return QVariant::fromValue( QgsGeometry( ml ) );
3278}
3279
3280static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3281{
3282 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3283
3284 if ( geom.isNull() )
3285 return QVariant();
3286
3287 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3288 if ( !curvePolygon && geom.isMultipart() )
3289 {
3290 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3291 {
3292 if ( collection->numGeometries() == 1 )
3293 {
3294 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3295 }
3296 }
3297 }
3298
3299 if ( !curvePolygon )
3300 return QVariant();
3301
3302 //idx is 1 based
3303 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3304
3305 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3306 return QVariant();
3307
3308 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3309 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3310 return result;
3311}
3312
3313static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3314{
3315 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3316
3317 if ( geom.isNull() )
3318 return QVariant();
3319
3320 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3321 if ( !collection )
3322 return QVariant();
3323
3324 //idx is 1 based
3325 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3326
3327 if ( idx < 0 || idx >= collection->numGeometries() )
3328 return QVariant();
3329
3330 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3331 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3332 return result;
3333}
3334
3335static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3336{
3337 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3338
3339 if ( geom.isNull() )
3340 return QVariant();
3341
3342 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3343 if ( !boundary )
3344 return QVariant();
3345
3346 return QVariant::fromValue( QgsGeometry( boundary ) );
3347}
3348
3349static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3350{
3351 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3352
3353 if ( geom.isNull() )
3354 return QVariant();
3355
3356 QgsGeometry merged = geom.mergeLines();
3357 if ( merged.isNull() )
3358 return QVariant();
3359
3360 return QVariant::fromValue( merged );
3361}
3362
3363static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3364{
3365 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3366 if ( geom.isNull() )
3367 return QVariant();
3368
3369 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3370 if ( geom2.isNull() )
3371 return QVariant();
3372
3373 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3374 if ( sharedPaths.isNull() )
3375 return QVariant();
3376
3377 return QVariant::fromValue( sharedPaths );
3378}
3379
3380
3381static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3382{
3383 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3384
3385 if ( geom.isNull() )
3386 return QVariant();
3387
3388 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3389
3390 QgsGeometry simplified = geom.simplify( tolerance );
3391 if ( simplified.isNull() )
3392 return QVariant();
3393
3394 return simplified;
3395}
3396
3397static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3398{
3399 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3400
3401 if ( geom.isNull() )
3402 return QVariant();
3403
3404 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3405
3407
3408 QgsGeometry simplified = simplifier.simplify( geom );
3409 if ( simplified.isNull() )
3410 return QVariant();
3411
3412 return simplified;
3413}
3414
3415static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3416{
3417 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3418
3419 if ( geom.isNull() )
3420 return QVariant();
3421
3422 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3423 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3424 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3425 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3426
3427 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3428 if ( smoothed.isNull() )
3429 return QVariant();
3430
3431 return smoothed;
3432}
3433
3434static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3435{
3436 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3437
3438 if ( geom.isNull() )
3439 return QVariant();
3440
3441 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3442 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3443 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3444
3445 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3446 if ( waved.isNull() )
3447 return QVariant();
3448
3449 return waved;
3450}
3451
3452static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3453{
3454 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3455
3456 if ( geom.isNull() )
3457 return QVariant();
3458
3459 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3460 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3461 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3462 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3463 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3464
3465 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3466 minAmplitude, maxAmplitude, seed );
3467 if ( waved.isNull() )
3468 return QVariant();
3469
3470 return waved;
3471}
3472
3473static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3474{
3475 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3476
3477 if ( geom.isNull() )
3478 return QVariant();
3479
3480 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3481 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3482 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3483
3484 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3485 if ( waved.isNull() )
3486 return QVariant();
3487
3488 return waved;
3489}
3490
3491static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3492{
3493 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3494
3495 if ( geom.isNull() )
3496 return QVariant();
3497
3498 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3499 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3500 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3501 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3502 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3503
3504 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3505 minAmplitude, maxAmplitude, seed );
3506 if ( waved.isNull() )
3507 return QVariant();
3508
3509 return waved;
3510}
3511
3512static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3513{
3514 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3515
3516 if ( geom.isNull() )
3517 return QVariant();
3518
3519 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3520 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3521 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3522
3523 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3524 if ( waved.isNull() )
3525 return QVariant();
3526
3527 return waved;
3528}
3529
3530static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3531{
3532 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3533
3534 if ( geom.isNull() )
3535 return QVariant();
3536
3537 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3538 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3539 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3540 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3541 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3542
3543 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3544 minAmplitude, maxAmplitude, seed );
3545 if ( waved.isNull() )
3546 return QVariant();
3547
3548 return waved;
3549}
3550
3551static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3552{
3553 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3554
3555 if ( geom.isNull() )
3556 return QVariant();
3557
3558 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3559 QVector< double > dashPattern;
3560 dashPattern.reserve( pattern.size() );
3561 for ( const QVariant &value : std::as_const( pattern ) )
3562 {
3563 bool ok = false;
3564 double v = value.toDouble( &ok );
3565 if ( ok )
3566 {
3567 dashPattern << v;
3568 }
3569 else
3570 {
3571 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3572 return QgsGeometry();
3573 }
3574 }
3575
3576 if ( dashPattern.size() % 2 != 0 )
3577 {
3578 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3579 return QgsGeometry();
3580 }
3581
3582 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3584 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3586 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3588 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3590 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3592 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3594 else
3595 {
3596 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3597 return QgsGeometry();
3598 }
3599
3600 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3602 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3604 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3606 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3608 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3610 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3612 else
3613 {
3614 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3615 return QgsGeometry();
3616 }
3617
3618 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3620 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3622 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3624 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3626 else
3627 {
3628 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3629 return QgsGeometry();
3630 }
3631
3632 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3633
3634 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3635 if ( result.isNull() )
3636 return QVariant();
3637
3638 return result;
3639}
3640
3641static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3642{
3643 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3644
3645 if ( geom.isNull() )
3646 return QVariant();
3647
3648 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3649 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3650 if ( densified.isNull() )
3651 return QVariant();
3652
3653 return densified;
3654}
3655
3656static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3657{
3658 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3659
3660 if ( geom.isNull() )
3661 return QVariant();
3662
3663 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3664 const QgsGeometry densified = geom.densifyByDistance( distance );
3665 if ( densified.isNull() )
3666 return QVariant();
3667
3668 return densified;
3669}
3670
3671static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3672{
3673 QVariantList list;
3674 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3675 {
3676 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3677 }
3678 else
3679 {
3680 list = values;
3681 }
3682
3683 QVector< QgsGeometry > parts;
3684 parts.reserve( list.size() );
3685 for ( const QVariant &value : std::as_const( list ) )
3686 {
3687 if ( value.userType() == qMetaTypeId< QgsGeometry>() )
3688 {
3689 parts << value.value<QgsGeometry>();
3690 }
3691 else
3692 {
3693 parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
3694 return QgsGeometry();
3695 }
3696 }
3697
3698 return QgsGeometry::collectGeometry( parts );
3699}
3700
3701static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3702{
3703 if ( values.count() < 2 || values.count() > 4 )
3704 {
3705 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3706 return QVariant();
3707 }
3708
3709 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3710 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3711 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3712 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3713 switch ( values.count() )
3714 {
3715 case 2:
3716 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3717 case 3:
3718 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3719 case 4:
3720 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3721 }
3722 return QVariant(); //avoid warning
3723}
3724
3725static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3726{
3727 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3728 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3729 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3730 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3731}
3732
3733static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3734{
3735 if ( values.empty() )
3736 {
3737 return QVariant();
3738 }
3739
3740 QVector<QgsPoint> points;
3741 points.reserve( values.count() );
3742
3743 auto addPoint = [&points]( const QgsGeometry & geom )
3744 {
3745 if ( geom.isNull() )
3746 return;
3747
3748 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3749 return;
3750
3751 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3752 if ( !point )
3753 return;
3754
3755 points << *point;
3756 };
3757
3758 for ( const QVariant &value : values )
3759 {
3760 if ( value.userType() == QMetaType::Type::QVariantList )
3761 {
3762 const QVariantList list = value.toList();
3763 for ( const QVariant &v : list )
3764 {
3765 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3766 }
3767 }
3768 else
3769 {
3770 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3771 }
3772 }
3773
3774 if ( points.count() < 2 )
3775 return QVariant();
3776
3777 return QgsGeometry( new QgsLineString( points ) );
3778}
3779
3780static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3781{
3782 if ( values.count() < 1 )
3783 {
3784 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3785 return QVariant();
3786 }
3787
3788 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3789
3790 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3791 return outerRing; // if it's already a polygon we have nothing to do
3792
3793 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3794 return QVariant();
3795
3796 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
3797
3798 const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
3799 if ( !exteriorRing && outerRing.isMultipart() )
3800 {
3801 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3802 {
3803 if ( collection->numGeometries() == 1 )
3804 {
3805 exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3806 }
3807 }
3808 }
3809
3810 if ( !exteriorRing )
3811 return QVariant();
3812
3813 polygon->setExteriorRing( exteriorRing->segmentize() );
3814
3815
3816 for ( int i = 1; i < values.count(); ++i )
3817 {
3818 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3819 if ( ringGeom.isNull() )
3820 continue;
3821
3822 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3823 continue;
3824
3825 const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
3826 if ( !ring && ringGeom.isMultipart() )
3827 {
3828 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3829 {
3830 if ( collection->numGeometries() == 1 )
3831 {
3832 ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3833 }
3834 }
3835 }
3836
3837 if ( !ring )
3838 continue;
3839
3840 polygon->addInteriorRing( ring->segmentize() );
3841 }
3842
3843 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3844}
3845
3846static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3847{
3848 std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3849 std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3850 lineString->clear();
3851
3852 for ( const QVariant &value : values )
3853 {
3854 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3855 if ( geom.isNull() )
3856 return QVariant();
3857
3858 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3859 return QVariant();
3860
3861 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3862 if ( !point && geom.isMultipart() )
3863 {
3864 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3865 {
3866 if ( collection->numGeometries() == 1 )
3867 {
3868 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3869 }
3870 }
3871 }
3872
3873 if ( !point )
3874 return QVariant();
3875
3876 lineString->addVertex( *point );
3877 }
3878
3879 tr->setExteriorRing( lineString.release() );
3880
3881 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3882}
3883
3884static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3885{
3886 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3887 if ( geom.isNull() )
3888 return QVariant();
3889
3890 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3891 return QVariant();
3892
3893 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3894 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3895
3896 if ( segment < 3 )
3897 {
3898 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3899 return QVariant();
3900 }
3901 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3902 if ( !point && geom.isMultipart() )
3903 {
3904 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3905 {
3906 if ( collection->numGeometries() == 1 )
3907 {
3908 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3909 }
3910 }
3911 }
3912 if ( !point )
3913 return QVariant();
3914
3915 QgsCircle circ( *point, radius );
3916 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3917}
3918
3919static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3920{
3921 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3922 if ( geom.isNull() )
3923 return QVariant();
3924
3925 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3926 return QVariant();
3927
3928 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3929 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3930 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3931 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3932 if ( segment < 3 )
3933 {
3934 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3935 return QVariant();
3936 }
3937 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3938 if ( !point && geom.isMultipart() )
3939 {
3940 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3941 {
3942 if ( collection->numGeometries() == 1 )
3943 {
3944 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3945 }
3946 }
3947 }
3948 if ( !point )
3949 return QVariant();
3950
3951 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3952 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3953}
3954
3955static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3956{
3957
3958 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3959 if ( pt1.isNull() )
3960 return QVariant();
3961
3962 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
3963 return QVariant();
3964
3965 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3966 if ( pt2.isNull() )
3967 return QVariant();
3968
3969 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
3970 return QVariant();
3971
3972 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3973 if ( nbEdges < 3 )
3974 {
3975 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
3976 return QVariant();
3977 }
3978
3979 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3981 {
3982 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
3983 return QVariant();
3984 }
3985
3986 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
3987 if ( !center && pt1.isMultipart() )
3988 {
3989 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
3990 {
3991 if ( collection->numGeometries() == 1 )
3992 {
3993 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3994 }
3995 }
3996 }
3997 if ( !center )
3998 return QVariant();
3999
4000 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
4001 if ( !corner && pt2.isMultipart() )
4002 {
4003 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
4004 {
4005 if ( collection->numGeometries() == 1 )
4006 {
4007 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4008 }
4009 }
4010 }
4011 if ( !corner )
4012 return QVariant();
4013
4014 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4015
4016 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4017
4018}
4019
4020static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4021{
4022 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4023 if ( pt1.isNull() )
4024 return QVariant();
4025 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4026 return QVariant();
4027
4028 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4029 if ( pt2.isNull() )
4030 return QVariant();
4031 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4032 return QVariant();
4033
4034 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4035 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4036 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4037
4038 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4039}
4040
4041static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4042{
4043 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4044 if ( pt1.isNull() )
4045 return QVariant();
4046 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4047 return QVariant();
4048
4049 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4050 if ( pt2.isNull() )
4051 return QVariant();
4052 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4053 return QVariant();
4054
4055 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4056 if ( pt3.isNull() )
4057 return QVariant();
4058 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4059 return QVariant();
4060
4061 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4062 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4063 {
4064 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4065 return QVariant();
4066 }
4067 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4068 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4069 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4070 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4071 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4072}
4073
4074static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4075{
4076 if ( geom.isNull() )
4077 return QVariant();
4078
4079 if ( idx < 0 )
4080 {
4081 idx += geom.constGet()->nCoordinates();
4082 }
4083 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4084 {
4085 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4086 return QVariant();
4087 }
4088 return QVariant::fromValue( geom.vertexAt( idx ) );
4089}
4090
4091// function used for the old $ style
4092static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4093{
4094 FEAT_FROM_CONTEXT( context, feature )
4095 const QgsGeometry geom = feature.geometry();
4096 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4097
4098 const QVariant v = pointAt( geom, idx, parent );
4099
4100 if ( !v.isNull() )
4101 return QVariant( v.value<QgsPoint>().x() );
4102 else
4103 return QVariant();
4104}
4105static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4106{
4107 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))
4108 {
4109 return fcnOldXat( values, f, parent, node );
4110 }
4111 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)
4112 {
4113 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4114 }
4115
4116 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4117 if ( geom.isNull() )
4118 {
4119 return QVariant();
4120 }
4121
4122 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4123
4124 const QVariant v = pointAt( geom, vertexNumber, parent );
4125 if ( !v.isNull() )
4126 return QVariant( v.value<QgsPoint>().x() );
4127 else
4128 return QVariant();
4129}
4130
4131// function used for the old $ style
4132static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4133{
4134 FEAT_FROM_CONTEXT( context, feature )
4135 const QgsGeometry geom = feature.geometry();
4136 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4137
4138 const QVariant v = pointAt( geom, idx, parent );
4139
4140 if ( !v.isNull() )
4141 return QVariant( v.value<QgsPoint>().y() );
4142 else
4143 return QVariant();
4144}
4145static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4146{
4147 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))
4148 {
4149 return fcnOldYat( values, f, parent, node );
4150 }
4151 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)
4152 {
4153 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4154 }
4155
4156 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4157 if ( geom.isNull() )
4158 {
4159 return QVariant();
4160 }
4161
4162 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4163
4164 const QVariant v = pointAt( geom, vertexNumber, parent );
4165 if ( !v.isNull() )
4166 return QVariant( v.value<QgsPoint>().y() );
4167 else
4168 return QVariant();
4169}
4170
4171static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4172{
4173 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4174 if ( geom.isNull() )
4175 {
4176 return QVariant();
4177 }
4178
4179 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4180
4181 const QVariant v = pointAt( geom, vertexNumber, parent );
4182 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4183 return QVariant( v.value<QgsPoint>().z() );
4184 else
4185 return QVariant();
4186}
4187
4188static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4189{
4190 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4191 if ( geom.isNull() )
4192 {
4193 return QVariant();
4194 }
4195
4196 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4197
4198 const QVariant v = pointAt( geom, vertexNumber, parent );
4199 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4200 return QVariant( v.value<QgsPoint>().m() );
4201 else
4202 return QVariant();
4203}
4204
4205
4206static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4207{
4208 if ( !context )
4209 return QVariant();
4210
4211 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4212 if ( context->hasGeometry() )
4213 return context->geometry();
4214 else
4215 {
4216 FEAT_FROM_CONTEXT( context, f )
4217 QgsGeometry geom = f.geometry();
4218 if ( !geom.isNull() )
4219 return QVariant::fromValue( geom );
4220 else
4221 return QVariant();
4222 }
4223}
4224
4225static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4226{
4227 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4228 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4229 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4230 return result;
4231}
4232
4233static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4234{
4235 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4236 if ( wkb.isNull() )
4237 return QVariant();
4238
4239 QgsGeometry geom;
4240 geom.fromWkb( wkb );
4241 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4242}
4243
4244static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4245{
4246 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4247 QgsOgcUtils::Context ogcContext;
4248 if ( context )
4249 {
4250 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4251 if ( mapLayerPtr )
4252 {
4253 ogcContext.layer = mapLayerPtr.data();
4254 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4255 }
4256 }
4257 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4258 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4259 return result;
4260}
4261
4262static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4263{
4264 FEAT_FROM_CONTEXT( context, f )
4266 QgsDistanceArea *calc = parent->geomCalculator();
4267 if ( calc )
4268 {
4269 double area = calc->measureArea( f.geometry() );
4270 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4271 return QVariant( area );
4272 }
4273 else
4274 {
4275 return QVariant( f.geometry().area() );
4276 }
4277}
4278
4279static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4280{
4281 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4282
4283 if ( geom.type() != Qgis::GeometryType::Polygon )
4284 return QVariant();
4285
4286 return QVariant( geom.area() );
4287}
4288
4289static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4290{
4291 FEAT_FROM_CONTEXT( context, f )
4293 QgsDistanceArea *calc = parent->geomCalculator();
4294 if ( calc )
4295 {
4296 double len = calc->measureLength( f.geometry() );
4297 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4298 return QVariant( len );
4299 }
4300 else
4301 {
4302 return QVariant( f.geometry().length() );
4303 }
4304}
4305
4306static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4307{
4308 FEAT_FROM_CONTEXT( context, f )
4310 QgsDistanceArea *calc = parent->geomCalculator();
4311 if ( calc )
4312 {
4313 double len = calc->measurePerimeter( f.geometry() );
4314 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4315 return QVariant( len );
4316 }
4317 else
4318 {
4319 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4320 }
4321}
4322
4323static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4324{
4325 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4326
4327 if ( geom.type() != Qgis::GeometryType::Polygon )
4328 return QVariant();
4329
4330 //length for polygons = perimeter
4331 return QVariant( geom.length() );
4332}
4333
4334static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4335{
4336 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4337 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4338}
4339
4340static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4341{
4342 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4343 if ( geom.isNull() )
4344 return QVariant();
4345
4346 return QVariant( geom.constGet()->partCount() );
4347}
4348
4349static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4350{
4351 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4352 if ( geom.isNull() )
4353 return QVariant();
4354
4355 return QVariant( geom.isMultipart() );
4356}
4357
4358static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4359{
4360 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4361
4362 if ( geom.isNull() )
4363 return QVariant();
4364
4365 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4366 if ( curvePolygon )
4367 return QVariant( curvePolygon->numInteriorRings() );
4368
4369 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4370 if ( collection )
4371 {
4372 //find first CurvePolygon in collection
4373 for ( int i = 0; i < collection->numGeometries(); ++i )
4374 {
4375 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4376 if ( !curvePolygon )
4377 continue;
4378
4379 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4380 }
4381 }
4382
4383 return QVariant();
4384}
4385
4386static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4387{
4388 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4389
4390 if ( geom.isNull() )
4391 return QVariant();
4392
4393 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4394 if ( curvePolygon )
4395 return QVariant( curvePolygon->ringCount() );
4396
4397 bool foundPoly = false;
4398 int ringCount = 0;
4399 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4400 if ( collection )
4401 {
4402 //find CurvePolygons in collection
4403 for ( int i = 0; i < collection->numGeometries(); ++i )
4404 {
4405 curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
4406 if ( !curvePolygon )
4407 continue;
4408
4409 foundPoly = true;
4410 ringCount += curvePolygon->ringCount();
4411 }
4412 }
4413
4414 if ( !foundPoly )
4415 return QVariant();
4416
4417 return QVariant( ringCount );
4418}
4419
4420static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4421{
4422 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4423 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4424 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4425 return result;
4426}
4427
4428static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4429{
4430 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4431 return QVariant::fromValue( geom.boundingBox().width() );
4432}
4433
4434static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4435{
4436 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4437 return QVariant::fromValue( geom.boundingBox().height() );
4438}
4439
4440static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4441{
4442 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4443 if ( geom.isNull() )
4444 return QVariant();
4445
4447}
4448
4449static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4450{
4451 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4452 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4453}
4454
4455static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4456{
4457 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4458 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4459}
4460
4461static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4462{
4463 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4464 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4465}
4466
4467static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4468{
4469 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4470 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4471}
4472
4473static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4474{
4475 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4476
4477 if ( geom.isNull() || geom.isEmpty( ) )
4478 return QVariant();
4479
4480 if ( !geom.constGet()->is3D() )
4481 return QVariant();
4482
4483 double max = std::numeric_limits< double >::lowest();
4484
4485 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4486 {
4487 double z = ( *it ).z();
4488
4489 if ( max < z )
4490 max = z;
4491 }
4492
4493 if ( max == std::numeric_limits< double >::lowest() )
4494 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4495
4496 return QVariant( max );
4497}
4498
4499static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4500{
4501 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4502
4503 if ( geom.isNull() || geom.isEmpty() )
4504 return QVariant();
4505
4506 if ( !geom.constGet()->is3D() )
4507 return QVariant();
4508
4509 double min = std::numeric_limits< double >::max();
4510
4511 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4512 {
4513 double z = ( *it ).z();
4514
4515 if ( z < min )
4516 min = z;
4517 }
4518
4519 if ( min == std::numeric_limits< double >::max() )
4520 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4521
4522 return QVariant( min );
4523}
4524
4525static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4526{
4527 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4528
4529 if ( geom.isNull() || geom.isEmpty() )
4530 return QVariant();
4531
4532 if ( !geom.constGet()->isMeasure() )
4533 return QVariant();
4534
4535 double min = std::numeric_limits< double >::max();
4536
4537 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4538 {
4539 double m = ( *it ).m();
4540
4541 if ( m < min )
4542 min = m;
4543 }
4544
4545 if ( min == std::numeric_limits< double >::max() )
4546 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4547
4548 return QVariant( min );
4549}
4550
4551static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4552{
4553 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4554
4555 if ( geom.isNull() || geom.isEmpty() )
4556 return QVariant();
4557
4558 if ( !geom.constGet()->isMeasure() )
4559 return QVariant();
4560
4561 double max = std::numeric_limits< double >::lowest();
4562
4563 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4564 {
4565 double m = ( *it ).m();
4566
4567 if ( max < m )
4568 max = m;
4569 }
4570
4571 if ( max == std::numeric_limits< double >::lowest() )
4572 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4573
4574 return QVariant( max );
4575}
4576
4577static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4578{
4579 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4580 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4581 if ( !curve )
4582 {
4583 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4584 return QVariant();
4585 }
4586
4587 return QVariant( curve->sinuosity() );
4588}
4589
4590static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4591{
4592 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4593 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4594 if ( !curve )
4595 {
4596 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4597 return QVariant();
4598 }
4599
4600 return QVariant( curve->straightDistance2d() );
4601}
4602
4603static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4604{
4605 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4606 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4607
4608 if ( !poly )
4609 {
4610 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4611 return QVariant();
4612 }
4613
4614 return QVariant( poly->roundness() );
4615}
4616
4617
4618
4619static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4620{
4621 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4622 if ( geom.isNull() )
4623 return QVariant();
4624
4625 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4626 flipped->swapXy();
4627 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4628}
4629
4630static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4631{
4632 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4633 if ( fGeom.isNull() )
4634 return QVariant();
4635
4636 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4637 if ( !curve && fGeom.isMultipart() )
4638 {
4639 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4640 {
4641 if ( collection->numGeometries() == 1 )
4642 {
4643 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4644 }
4645 }
4646 }
4647
4648 if ( !curve )
4649 return QVariant();
4650
4651 return QVariant::fromValue( curve->isClosed() );
4652}
4653
4654static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4655{
4656 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4657
4658 if ( geom.isNull() )
4659 return QVariant();
4660
4661 QVariant result;
4662 if ( !geom.isMultipart() )
4663 {
4664 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4665
4666 if ( !line )
4667 return QVariant();
4668
4669 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4670 closedLine->close();
4671
4672 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4673 }
4674 else
4675 {
4676 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4677
4678 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4679
4680 for ( int i = 0; i < collection->numGeometries(); ++i )
4681 {
4682 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4683 {
4684 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4685 closedLine->close();
4686
4687 closed->addGeometry( closedLine.release() );
4688 }
4689 }
4690 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4691 }
4692
4693 return result;
4694}
4695
4696static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4697{
4698 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4699 if ( fGeom.isNull() )
4700 return QVariant();
4701
4702 return QVariant::fromValue( fGeom.isEmpty() );
4703}
4704
4705static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4706{
4707 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4708 return QVariant::fromValue( true );
4709
4710 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4711 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4712}
4713
4714static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4715{
4716 if ( values.length() < 2 || values.length() > 3 )
4717 return QVariant();
4718
4719 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4720 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4721
4722 if ( fGeom.isNull() || sGeom.isNull() )
4723 return QVariant();
4724
4725 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4726
4727 if ( values.length() == 2 )
4728 {
4729 //two geometry arguments, return relation
4730 QString result = engine->relate( sGeom.constGet() );
4731 return QVariant::fromValue( result );
4732 }
4733 else
4734 {
4735 //three arguments, test pattern
4736 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4737 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4738 return QVariant::fromValue( result );
4739 }
4740}
4741
4742static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4743{
4744 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4745 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4746 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4747}
4748static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4749{
4750 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4751 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4752 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4753}
4754static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4755{
4756 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4757 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4758 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4759}
4760static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4761{
4762 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4763 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4764 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4765}
4766static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4767{
4768 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4769 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4770 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4771}
4772static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4773{
4774 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4775 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4776 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4777}
4778static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4779{
4780 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4781 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4782 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4783}
4784static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4785{
4786 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4787 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4788 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4789}
4790
4791static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4792{
4793 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4794 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4795 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4796 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4797 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4798 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4799
4801 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4802 capStyle = Qgis::EndCapStyle::Flat;
4803 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4804 capStyle = Qgis::EndCapStyle::Square;
4805
4807 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4808 joinStyle = Qgis::JoinStyle::Miter;
4809 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4810 joinStyle = Qgis::JoinStyle::Bevel;
4811
4812 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4813 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4814 return result;
4815}
4816
4817static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4818{
4819 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4820 const QgsGeometry reoriented = fGeom.forceRHR();
4821 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4822}
4823
4824static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4825{
4826 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4827 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4828 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4829}
4830
4831static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4832{
4833 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4834 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4835 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4836}
4837
4838static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4839{
4840 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4841 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4842 if ( !pt && fGeom.isMultipart() )
4843 {
4844 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4845 {
4846 if ( collection->numGeometries() == 1 )
4847 {
4848 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4849 }
4850 }
4851 }
4852
4853 if ( !pt )
4854 {
4855 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4856 return QVariant();
4857 }
4858
4859 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4860 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4861 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4862 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4863
4864 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4865 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4866 return result;
4867}
4868
4869static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4870{
4871 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4872 if ( fGeom.type() != Qgis::GeometryType::Line )
4873 {
4874 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4875 return QVariant();
4876 }
4877
4878 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4879 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4880 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4881
4882 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4883 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4884 return result;
4885}
4886
4887static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4888{
4889 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4890 if ( fGeom.type() != Qgis::GeometryType::Line )
4891 {
4892 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4893 return QVariant();
4894 }
4895
4896 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4897
4898 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4899 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4900 return result;
4901}
4902
4903static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4904{
4905 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4906 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4907 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4908 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4909 if ( joinInt < 1 || joinInt > 3 )
4910 return QVariant();
4911 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4912
4913 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4914
4915 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4916 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4917 return result;
4918}
4919
4920static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4921{
4922 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4923 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4924 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4925
4926 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4927 if ( joinInt < 1 || joinInt > 3 )
4928 return QVariant();
4929 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4930
4931 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4932
4933 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
4934 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4935 return result;
4936}
4937
4938static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4939{
4940 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4941 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4942 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4943
4944 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
4945 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4946 return result;
4947}
4948
4949static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4950{
4951 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4952 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4953 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4954 fGeom.translate( dx, dy );
4955 return QVariant::fromValue( fGeom );
4956}
4957
4958static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4959{
4960 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4961 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4962 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
4963 : QgsGeometry();
4964 const bool perPart = values.value( 3 ).toBool();
4965
4966 if ( center.isNull() && perPart && fGeom.isMultipart() )
4967 {
4968 // no explicit center, rotating per part
4969 // (note that we only do this branch for multipart geometries -- for singlepart geometries
4970 // the result is equivalent to setting perPart as false anyway)
4971 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
4972 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
4973 {
4974 const QgsPointXY partCenter = ( *it )->boundingBox().center();
4975 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
4976 t.rotate( -rotation );
4977 t.translate( -partCenter.x(), -partCenter.y() );
4978 ( *it )->transform( t );
4979 }
4980 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
4981 }
4982 else
4983 {
4984 QgsPointXY pt;
4985 if ( center.isEmpty() )
4986 {
4987 // if center wasn't specified, use bounding box centroid
4988 pt = fGeom.boundingBox().center();
4989 }
4991 {
4992 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
4993 return QVariant();
4994 }
4995 else
4996 {
4997 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
4998 }
4999
5000 fGeom.rotate( rotation, pt );
5001 return QVariant::fromValue( fGeom );
5002 }
5003}
5004
5005static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5006{
5007 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5008 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5009 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5010 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5011 : QgsGeometry();
5012
5013 QgsPointXY pt;
5014 if ( center.isNull() )
5015 {
5016 // if center wasn't specified, use bounding box centroid
5017 pt = fGeom.boundingBox().center();
5018 }
5020 {
5021 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5022 return QVariant();
5023 }
5024 else
5025 {
5026 pt = center.asPoint();
5027 }
5028
5029 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5030 t.scale( xScale, yScale );
5031 t.translate( -pt.x(), -pt.y() );
5032 fGeom.transform( t );
5033 return QVariant::fromValue( fGeom );
5034}
5035
5036static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5037{
5038 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5039 if ( fGeom.isNull() )
5040 {
5041 return QVariant();
5042 }
5043
5044 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5045 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5046
5047 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5048
5049 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5050 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5051
5052 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5053 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5054 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5055 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5056
5057 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5058 {
5059 fGeom.get()->addZValue( 0 );
5060 }
5061 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5062 {
5063 fGeom.get()->addMValue( 0 );
5064 }
5065
5066 QTransform transform;
5067 transform.translate( deltaX, deltaY );
5068 transform.rotate( rotationZ );
5069 transform.scale( scaleX, scaleY );
5070 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5071
5072 return QVariant::fromValue( fGeom );
5073}
5074
5075
5076static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5077{
5078 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5079 QgsGeometry geom = fGeom.centroid();
5080 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5081 return result;
5082}
5083static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5084{
5085 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5086 QgsGeometry geom = fGeom.pointOnSurface();
5087 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5088 return result;
5089}
5090
5091static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5092{
5093 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5094 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5095 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5096 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5097 return result;
5098}
5099
5100static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5101{
5102 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5103 QgsGeometry geom = fGeom.convexHull();
5104 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5105 return result;
5106}
5107
5108#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5109static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5110{
5111 try
5112 {
5113 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5114 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5115 const bool allowHoles = values.value( 2 ).toBool();
5116 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5117 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5118 return result;
5119 }
5120 catch ( QgsCsException &cse )
5121 {
5122 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5123 return QVariant();
5124 }
5125}
5126#endif
5127
5128static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5129{
5130 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5131 int segments = 36;
5132 if ( values.length() == 2 )
5133 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5134 if ( segments < 0 )
5135 {
5136 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5137 return QVariant();
5138 }
5139
5140 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5141 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5142 return result;
5143}
5144
5145static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5146{
5147 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5149 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5150 return result;
5151}
5152
5153static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5154{
5155 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5156
5157 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5158 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5159 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5160
5161 double area, angle, width, height;
5162 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5163
5164 if ( geom.isNull() )
5165 {
5166 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5167 return QVariant();
5168 }
5169 return angle;
5170}
5171
5172static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5173{
5174 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5175 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5176 QgsGeometry geom = fGeom.difference( sGeom );
5177 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5178 return result;
5179}
5180
5181static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5182{
5183 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5184 if ( fGeom.isNull() )
5185 return QVariant();
5186
5187 QVariant result;
5188 if ( !fGeom.isMultipart() )
5189 {
5190 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5191 if ( !curve )
5192 return QVariant();
5193
5194 QgsCurve *reversed = curve->reversed();
5195 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5196 }
5197 else
5198 {
5199 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5200 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5201 for ( int i = 0; i < collection->numGeometries(); ++i )
5202 {
5203 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5204 {
5205 reversed->addGeometry( curve->reversed() );
5206 }
5207 else
5208 {
5209 reversed->addGeometry( collection->geometryN( i )->clone() );
5210 }
5211 }
5212 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5213 }
5214 return result;
5215}
5216
5217static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5218{
5219 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5220 if ( fGeom.isNull() )
5221 return QVariant();
5222
5223 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5224 if ( !curvePolygon && fGeom.isMultipart() )
5225 {
5226 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5227 {
5228 if ( collection->numGeometries() == 1 )
5229 {
5230 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5231 }
5232 }
5233 }
5234
5235 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5236 return QVariant();
5237
5238 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5239 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5240 return result;
5241}
5242
5243static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5244{
5245 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5246 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5247 return QVariant( fGeom.distance( sGeom ) );
5248}
5249
5250static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5251{
5252 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5253 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5254
5255 double res = -1;
5256 if ( values.length() == 3 && values.at( 2 ).isValid() )
5257 {
5258 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5259 densify = std::clamp( densify, 0.0, 1.0 );
5260 res = g1.hausdorffDistanceDensify( g2, densify );
5261 }
5262 else
5263 {
5264 res = g1.hausdorffDistance( g2 );
5265 }
5266
5267 return res > -1 ? QVariant( res ) : QVariant();
5268}
5269
5270static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5271{
5272 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5273 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5274 QgsGeometry geom = fGeom.intersection( sGeom );
5275 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5276 return result;
5277}
5278static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5279{
5280 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5281 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5282 QgsGeometry geom = fGeom.symDifference( sGeom );
5283 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5284 return result;
5285}
5286static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5287{
5288 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5289 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5290 QgsGeometry geom = fGeom.combine( sGeom );
5291 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5292 return result;
5293}
5294
5295static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5296{
5297 if ( values.length() < 1 || values.length() > 2 )
5298 return QVariant();
5299
5300 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5301 int prec = 8;
5302 if ( values.length() == 2 )
5303 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5304 QString wkt = fGeom.asWkt( prec );
5305 return QVariant( wkt );
5306}
5307
5308static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5309{
5310 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5311 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5312}
5313
5314static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5315{
5316 if ( values.length() != 2 )
5317 {
5318 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5319 return QVariant();
5320 }
5321
5322 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5323 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5324
5325 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5326 if ( !pt1 && fGeom1.isMultipart() )
5327 {
5328 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5329 {
5330 if ( collection->numGeometries() == 1 )
5331 {
5332 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5333 }
5334 }
5335 }
5336
5337 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5338 if ( !pt2 && fGeom2.isMultipart() )
5339 {
5340 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5341 {
5342 if ( collection->numGeometries() == 1 )
5343 {
5344 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5345 }
5346 }
5347 }
5348
5349 if ( !pt1 || !pt2 )
5350 {
5351 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5352 return QVariant();
5353 }
5354
5355 // Code from PostGIS
5356 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5357 {
5358 if ( pt1->y() < pt2->y() )
5359 return 0.0;
5360 else if ( pt1->y() > pt2->y() )
5361 return M_PI;
5362 else
5363 return 0;
5364 }
5365
5366 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5367 {
5368 if ( pt1->x() < pt2->x() )
5369 return M_PI_2;
5370 else if ( pt1->x() > pt2->x() )
5371 return M_PI + ( M_PI_2 );
5372 else
5373 return 0;
5374 }
5375
5376 if ( pt1->x() < pt2->x() )
5377 {
5378 if ( pt1->y() < pt2->y() )
5379 {
5380 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5381 }
5382 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5383 {
5384 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5385 + ( M_PI_2 );
5386 }
5387 }
5388
5389 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5390 {
5391 if ( pt1->y() > pt2->y() )
5392 {
5393 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5394 + M_PI;
5395 }
5396 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5397 {
5398 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5399 + ( M_PI + ( M_PI_2 ) );
5400 }
5401 }
5402}
5403
5404static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5405{
5406 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5407 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5408 QString sourceCrs = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5409 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5410
5411 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5412 {
5413 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5414 return QVariant();
5415 }
5416
5417 const QgsPointXY point1 = geom1.asPoint();
5418 const QgsPointXY point2 = geom2.asPoint();
5419 if ( point1.isEmpty() || point2.isEmpty() )
5420 {
5421 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5422 return QVariant();
5423 }
5424
5426 if ( context )
5427 {
5428 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5429
5430 if ( sourceCrs.isEmpty() )
5431 {
5432 sourceCrs = context->variable( QStringLiteral( "layer_crs" ) ).toString();
5433 }
5434
5435 if ( ellipsoid.isEmpty() )
5436 {
5437 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5438 }
5439 }
5440
5442 if ( !sCrs.isValid() )
5443 {
5444 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5445 return QVariant();
5446 }
5447
5448 QgsDistanceArea da;
5449 da.setSourceCrs( sCrs, tContext );
5450 if ( !da.setEllipsoid( ellipsoid ) )
5451 {
5452 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5453 return QVariant();
5454 }
5455
5456 try
5457 {
5458 const double bearing = da.bearing( point1, point2 );
5459 if ( std::isfinite( bearing ) )
5460 {
5461 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5462 }
5463 }
5464 catch ( QgsCsException &cse )
5465 {
5466 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5467 return QVariant();
5468 }
5469 return QVariant();
5470}
5471
5472static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5473{
5474 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5475
5477 {
5478 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5479 return QVariant();
5480 }
5481
5482 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5483 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5484 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5485
5486 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5487 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5488
5489 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5490}
5491
5492static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5493{
5494 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5495 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5496
5497 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5498 if ( !pt1 && fGeom1.isMultipart() )
5499 {
5500 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5501 {
5502 if ( collection->numGeometries() == 1 )
5503 {
5504 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5505 }
5506 }
5507 }
5508 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5509 if ( !pt2 && fGeom2.isMultipart() )
5510 {
5511 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5512 {
5513 if ( collection->numGeometries() == 1 )
5514 {
5515 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5516 }
5517 }
5518 }
5519
5520 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5521 !pt1 || !pt2 )
5522 {
5523 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5524 return QVariant();
5525 }
5526
5527 return pt1->inclination( *pt2 );
5528
5529}
5530
5531static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5532{
5533 if ( values.length() != 3 )
5534 return QVariant();
5535
5536 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5537 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5538 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5539
5540 QgsGeometry geom = fGeom.extrude( x, y );
5541
5542 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5543 return result;
5544}
5545
5546static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5547{
5548 if ( values.length() < 2 )
5549 return QVariant();
5550
5551 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5552
5553 if ( !fGeom.isMultipart() )
5554 return values.at( 0 );
5555
5556 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5557 QVariant cachedExpression;
5558 if ( ctx )
5559 cachedExpression = ctx->cachedValue( expString );
5560 QgsExpression expression;
5561
5562 if ( cachedExpression.isValid() )
5563 {
5564 expression = cachedExpression.value<QgsExpression>();
5565 }
5566 else
5567 expression = QgsExpression( expString );
5568
5569 bool asc = values.value( 2 ).toBool();
5570
5571 QgsExpressionContext *unconstedContext = nullptr;
5572 QgsFeature f;
5573 if ( ctx )
5574 {
5575 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5576 // so no reason to worry
5577 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5578 f = ctx->feature();
5579 }
5580 else
5581 {
5582 // If there's no context provided, create a fake one
5583 unconstedContext = new QgsExpressionContext();
5584 }
5585
5586 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5587 Q_ASSERT( collection ); // Should have failed the multipart check above
5588
5590 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5591 QgsExpressionSorter sorter( orderBy );
5592
5593 QList<QgsFeature> partFeatures;
5594 partFeatures.reserve( collection->partCount() );
5595 for ( int i = 0; i < collection->partCount(); ++i )
5596 {
5597 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5598 partFeatures << f;
5599 }
5600
5601 sorter.sortFeatures( partFeatures, unconstedContext );
5602
5603 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5604
5605 Q_ASSERT( orderedGeom );
5606
5607 while ( orderedGeom->partCount() )
5608 orderedGeom->removeGeometry( 0 );
5609
5610 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5611 {
5612 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5613 }
5614
5615 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5616
5617 if ( !ctx )
5618 delete unconstedContext;
5619
5620 return result;
5621}
5622
5623static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5624{
5625 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5626 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5627
5628 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5629
5630 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5631 return result;
5632}
5633
5634static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5635{
5636 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5637 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5638
5639 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5640
5641 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5642 return result;
5643}
5644
5645static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5646{
5647 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5648 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5649
5650 QgsGeometry geom = lineGeom.interpolate( distance );
5651
5652 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5653 return result;
5654}
5655
5656static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5657{
5658 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5659 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5660 const bool use3DDistance = values.at( 2 ).toBool();
5661
5662 double x, y, z, distance;
5663
5664 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5665 if ( !line )
5666 {
5667 return QVariant();
5668 }
5669
5670 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
5671 {
5672 QgsPoint point( x, y );
5673 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
5674 {
5675 point.addZValue( z );
5676 }
5677 return QVariant::fromValue( QgsGeometry( point.clone() ) );
5678 }
5679
5680 return QVariant();
5681}
5682
5683static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5684{
5685 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5686 if ( lineGeom.type() != Qgis::GeometryType::Line )
5687 {
5688 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5689 return QVariant();
5690 }
5691
5692 const QgsCurve *curve = nullptr;
5693 if ( !lineGeom.isMultipart() )
5694 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5695 else
5696 {
5697 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5698 {
5699 if ( collection->numGeometries() > 0 )
5700 {
5701 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5702 }
5703 }
5704 }
5705 if ( !curve )
5706 return QVariant();
5707
5708 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5709 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5710
5711 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5712 QgsGeometry result( std::move( substring ) );
5713 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5714}
5715
5716static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5717{
5718 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5719 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5720
5721 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5722}
5723
5724static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5725{
5726 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5727 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5728 if ( vertex < 0 )
5729 {
5730 //negative idx
5731 int count = geom.constGet()->nCoordinates();
5732 vertex = count + vertex;
5733 }
5734
5735 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5736}
5737
5738static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5739{
5740 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5741 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5742 if ( vertex < 0 )
5743 {
5744 //negative idx
5745 int count = geom.constGet()->nCoordinates();
5746 vertex = count + vertex;
5747 }
5748
5749 return geom.distanceToVertex( vertex );
5750}
5751
5752static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5753{
5754 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5755 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5756
5757 double distance = lineGeom.lineLocatePoint( pointGeom );
5758
5759 return distance >= 0 ? distance : QVariant();
5760}
5761
5762static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5763{
5764 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5765 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5766 const bool use3DDistance = values.at( 2 ).toBool();
5767
5768 double x, y, z, distance;
5769
5770 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5771 if ( !line )
5772 {
5773 return QVariant();
5774 }
5775
5776 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
5777 return found ? distance : QVariant();
5778}
5779
5780static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5781{
5782 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5783 {
5784 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5785 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5786 }
5787
5788 if ( values.length() >= 1 )
5789 {
5790 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5791 return QVariant( qlonglong( std::round( number ) ) );
5792 }
5793
5794 return QVariant();
5795}
5796
5797static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5798{
5799 Q_UNUSED( values )
5800 Q_UNUSED( parent )
5801 return M_PI;
5802}
5803
5804static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5805{
5806 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5807 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5808 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5809 if ( places < 0 )
5810 {
5811 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5812 return QVariant();
5813 }
5814
5815 const bool omitGroupSeparator = values.value( 3 ).toBool();
5816 const bool trimTrailingZeros = values.value( 4 ).toBool();
5817
5818 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5819 if ( !omitGroupSeparator )
5820 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5821 else
5822 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5823
5824 QString res = locale.toString( value, 'f', places );
5825
5826 if ( trimTrailingZeros )
5827 {
5828#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5829 const QChar decimal = locale.decimalPoint();
5830 const QChar zeroDigit = locale.zeroDigit();
5831#else
5832 const QChar decimal = locale.decimalPoint().at( 0 );
5833 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5834#endif
5835
5836 if ( res.contains( decimal ) )
5837 {
5838 int trimPoint = res.length() - 1;
5839
5840 while ( res.at( trimPoint ) == zeroDigit )
5841 trimPoint--;
5842
5843 if ( res.at( trimPoint ) == decimal )
5844 trimPoint--;
5845
5846 res.truncate( trimPoint + 1 );
5847 }
5848 }
5849
5850 return res;
5851}
5852
5853static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5854{
5855 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5856 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5857 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5858
5859 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
5860 if ( format.indexOf( "Z" ) > 0 )
5861 datetime = datetime.toUTC();
5862
5863 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5864 return locale.toString( datetime, format );
5865}
5866
5867static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5868{
5869 const QVariant variant = values.at( 0 );
5870 bool isQColor;
5871 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
5872 if ( !color.isValid() )
5873 return QVariant();
5874
5875 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5876 if ( color.spec() == QColor::Spec::Cmyk )
5877 {
5878 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5879 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
5880 }
5881 else
5882 {
5883 const float avg = ( color.redF() + color.greenF() + color.blueF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5884 color.setRgbF( avg, avg, avg, alpha );
5885 }
5886
5887 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
5888}
5889
5890static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5891{
5892 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5893 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5894 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5895 if ( ratio > 1 )
5896 {
5897 ratio = 1;
5898 }
5899 else if ( ratio < 0 )
5900 {
5901 ratio = 0;
5902 }
5903
5904 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5905 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5906 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5907 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5908
5909 QColor newColor( red, green, blue, alpha );
5910
5911 return QgsSymbolLayerUtils::encodeColor( newColor );
5912}
5913
5914static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5915{
5916 const QVariant variant1 = values.at( 0 );
5917 const QVariant variant2 = values.at( 1 );
5918
5919 if ( variant1.userType() != variant2.userType() )
5920 {
5921 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
5922 return QVariant();
5923 }
5924
5925 bool isQColor;
5926 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
5927 if ( !color1.isValid() )
5928 return QVariant();
5929
5930 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
5931 if ( !color2.isValid() )
5932 return QVariant();
5933
5934 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
5935 {
5936 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
5937 return QVariant();
5938 }
5939
5940 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
5941
5942 // TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5943 // NOLINTBEGIN(bugprone-narrowing-conversions)
5944
5945 QColor newColor;
5946 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
5947 if ( color1.spec() == QColor::Spec::Cmyk )
5948 {
5949 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
5950 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
5951 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
5952 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
5953 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
5954 }
5955 else
5956 {
5957 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
5958 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
5959 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
5960 newColor = QColor::fromRgbF( red, green, blue, alpha );
5961 }
5962
5963 // NOLINTEND(bugprone-narrowing-conversions)
5964
5965 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
5966}
5967
5968static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5969{
5970 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
5971 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5972 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5973 QColor color = QColor( red, green, blue );
5974 if ( ! color.isValid() )
5975 {
5976 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
5977 color = QColor( 0, 0, 0 );
5978 }
5979
5980 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5981}
5982
5983static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5984{
5985 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
5986 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
5987 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
5988 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
5989 QColor color = QColor::fromRgbF( red, green, blue, alpha );
5990 if ( ! color.isValid() )
5991 {
5992 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
5993 return QVariant();
5994 }
5995
5996 return color;
5997}
5998
5999static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6000{
6001 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6002 QVariant value = node->eval( parent, context );
6003 if ( parent->hasEvalError() )
6004 {
6005 parent->setEvalErrorString( QString() );
6006 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6008 value = node->eval( parent, context );
6010 }
6011 return value;
6012}
6013
6014static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6015{
6016 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6018 QVariant value = node->eval( parent, context );
6020 if ( value.toBool() )
6021 {
6022 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6024 value = node->eval( parent, context );
6026 }
6027 else
6028 {
6029 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6031 value = node->eval( parent, context );
6033 }
6034 return value;
6035}
6036
6037static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6038{
6039 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6040 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6041 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6042 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6043 QColor color = QColor( red, green, blue, alpha );
6044 if ( ! color.isValid() )
6045 {
6046 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6047 color = QColor( 0, 0, 0 );
6048 }
6049 return QgsSymbolLayerUtils::encodeColor( color );
6050}
6051
6052QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6053{
6054 QgsGradientColorRamp expRamp;
6055 const QgsColorRamp *ramp = nullptr;
6056 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6057 {
6058 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6059 ramp = &expRamp;
6060 }
6061 else
6062 {
6063 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6064 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6065 if ( ! ramp )
6066 {
6067 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6068 return QVariant();
6069 }
6070 }
6071
6072 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6073 QColor color = ramp->color( value );
6074 return QgsSymbolLayerUtils::encodeColor( color );
6075}
6076
6077static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6078{
6079 // Hue ranges from 0 - 360
6080 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6081 // Saturation ranges from 0 - 100
6082 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6083 // Lightness ranges from 0 - 100
6084 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6085
6086 QColor color = QColor::fromHslF( hue, saturation, lightness );
6087
6088 if ( ! color.isValid() )
6089 {
6090 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6091 color = QColor( 0, 0, 0 );
6092 }
6093
6094 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6095}
6096
6097static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6098{
6099 // Hue ranges from 0 - 360
6100 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6101 // Saturation ranges from 0 - 100
6102 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6103 // Lightness ranges from 0 - 100
6104 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6105 // Alpha ranges from 0 - 255
6106 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6107
6108 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6109 if ( ! color.isValid() )
6110 {
6111 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6112 color = QColor( 0, 0, 0 );
6113 }
6114 return QgsSymbolLayerUtils::encodeColor( color );
6115}
6116
6117static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6118{
6119 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6120 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6121 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6122 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6123
6124 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6125 if ( ! color.isValid() )
6126 {
6127 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6128 return QVariant();
6129 }
6130
6131 return color;
6132}
6133
6134static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6135{
6136 // Hue ranges from 0 - 360
6137 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6138 // Saturation ranges from 0 - 100
6139 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6140 // Value ranges from 0 - 100
6141 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6142
6143 QColor color = QColor::fromHsvF( hue, saturation, value );
6144
6145 if ( ! color.isValid() )
6146 {
6147 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6148 color = QColor( 0, 0, 0 );
6149 }
6150
6151 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6152}
6153
6154static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6155{
6156 // Hue ranges from 0 - 360
6157 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6158 // Saturation ranges from 0 - 100
6159 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6160 // Value ranges from 0 - 100
6161 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6162 // Alpha ranges from 0 - 255
6163 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6164
6165 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6166 if ( ! color.isValid() )
6167 {
6168 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6169 color = QColor( 0, 0, 0 );
6170 }
6171 return QgsSymbolLayerUtils::encodeColor( color );
6172}
6173
6174static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6175{
6176 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6177 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6178 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6179 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6180 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6181
6182 if ( ! color.isValid() )
6183 {
6184 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6185 return QVariant();
6186 }
6187
6188 return color;
6189}
6190
6191static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6192{
6193 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6194 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6195 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6196 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6197 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6198
6199 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6200 if ( ! color.isValid() )
6201 {
6202 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6203 return QVariant();
6204 }
6205
6206 return color;
6207}
6208
6209static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6210{
6211 // Cyan ranges from 0 - 100
6212 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6213 // Magenta ranges from 0 - 100
6214 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6215 // Yellow ranges from 0 - 100
6216 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6217 // Black ranges from 0 - 100
6218 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6219
6220 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6221
6222 if ( ! color.isValid() )
6223 {
6224 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6225 color = QColor( 0, 0, 0 );
6226 }
6227
6228 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6229}
6230
6231static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6232{
6233 // Cyan ranges from 0 - 100
6234 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6235 // Magenta ranges from 0 - 100
6236 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6237 // Yellow ranges from 0 - 100
6238 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6239 // Black ranges from 0 - 100
6240 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6241 // Alpha ranges from 0 - 255
6242 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6243
6244 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6245 if ( ! color.isValid() )
6246 {
6247 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6248 color = QColor( 0, 0, 0 );
6249 }
6250 return QgsSymbolLayerUtils::encodeColor( color );
6251}
6252
6253static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6254{
6255 const QVariant variant = values.at( 0 );
6256 bool isQColor;
6257 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6258 if ( !color.isValid() )
6259 return QVariant();
6260
6261 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6262 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6263 return color.red();
6264 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6265 return color.green();
6266 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6267 return color.blue();
6268 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6269 return color.alpha();
6270 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6271 return static_cast< double >( color.hsvHueF() * 360 );
6272 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6273 return static_cast< double >( color.hsvSaturationF() * 100 );
6274 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6275 return static_cast< double >( color.valueF() * 100 );
6276 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6277 return static_cast< double >( color.hslHueF() * 360 );
6278 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6279 return static_cast< double >( color.hslSaturationF() * 100 );
6280 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6281 return static_cast< double >( color.lightnessF() * 100 );
6282 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6283 return static_cast< double >( color.cyanF() * 100 );
6284 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6285 return static_cast< double >( color.magentaF() * 100 );
6286 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6287 return static_cast< double >( color.yellowF() * 100 );
6288 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6289 return static_cast< double >( color.blackF() * 100 );
6290
6291 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6292 return QVariant();
6293}
6294
6295static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6296{
6297 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6298 if ( map.empty() )
6299 {
6300 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6301 return QVariant();
6302 }
6303
6304 QList< QColor > colors;
6306 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6307 {
6308 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6309 if ( !colors.last().isValid() )
6310 {
6311 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6312 return QVariant();
6313 }
6314
6315 double step = it.key().toDouble();
6316 if ( it == map.constBegin() )
6317 {
6318 if ( step != 0.0 )
6319 stops << QgsGradientStop( step, colors.last() );
6320 }
6321 else if ( it == map.constEnd() )
6322 {
6323 if ( step != 1.0 )
6324 stops << QgsGradientStop( step, colors.last() );
6325 }
6326 else
6327 {
6328 stops << QgsGradientStop( step, colors.last() );
6329 }
6330 }
6331 bool discrete = values.at( 1 ).toBool();
6332
6333 if ( colors.empty() )
6334 return QVariant();
6335
6336 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6337}
6338
6339static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6340{
6341 const QVariant variant = values.at( 0 );
6342 bool isQColor;
6343 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6344 if ( !color.isValid() )
6345 return QVariant();
6346
6347 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6348 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6349 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6350 color.setRed( std::clamp( value, 0, 255 ) );
6351 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6352 color.setGreen( std::clamp( value, 0, 255 ) );
6353 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6354 color.setBlue( std::clamp( value, 0, 255 ) );
6355 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6356 color.setAlpha( std::clamp( value, 0, 255 ) );
6357 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6358 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6359 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6360 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6361 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6362 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6363 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6364 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6365 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6366 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6367 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6368 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6369 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6370 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6371 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6372 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6373 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6374 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6375 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6376 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6377 else
6378 {
6379 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6380 return QVariant();
6381 }
6382 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6383}
6384
6385static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6386{
6387 const QVariant variant = values.at( 0 );
6388 bool isQColor;
6389 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6390 if ( !color.isValid() )
6391 return QVariant();
6392
6393 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6394
6395 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6396}
6397
6398static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6399{
6400 const QVariant variant = values.at( 0 );
6401 bool isQColor;
6402 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6403 if ( !color.isValid() )
6404 return QVariant();
6405
6406 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6407
6408 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6409}
6410
6411static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6412{
6413 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6414 QgsGeometry geom = feat.geometry();
6415 if ( !geom.isNull() )
6416 return QVariant::fromValue( geom );
6417 return QVariant();
6418}
6419
6420static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6421{
6422 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6423 if ( !feat.isValid() )
6424 return QVariant();
6425 return feat.id();
6426}
6427
6428static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6429{
6430 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6431 QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6432 QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6433
6435 if ( ! s.isValid() )
6436 return QVariant::fromValue( fGeom );
6438 if ( ! d.isValid() )
6439 return QVariant::fromValue( fGeom );
6440
6442 if ( context )
6443 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6444 QgsCoordinateTransform t( s, d, tContext );
6445 try
6446 {
6448 return QVariant::fromValue( fGeom );
6449 }
6450 catch ( QgsCsException &cse )
6451 {
6452 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6453 return QVariant();
6454 }
6455 return QVariant();
6456}
6457
6458
6459static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6460{
6461 bool foundLayer = false;
6462 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6463
6464 //no layer found
6465 if ( !featureSource || !foundLayer )
6466 {
6467 return QVariant();
6468 }
6469
6470 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6471
6473 req.setFilterFid( fid );
6474 req.setTimeout( 10000 );
6475 req.setRequestMayBeNested( true );
6476 if ( context )
6477 req.setFeedback( context->feedback() );
6478 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6479
6480 QgsFeature fet;
6481 QVariant result;
6482 if ( fIt.nextFeature( fet ) )
6483 result = QVariant::fromValue( fet );
6484
6485 return result;
6486}
6487
6488static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6489{
6490 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6491 bool foundLayer = false;
6492 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6493
6494 //no layer found
6495 if ( !featureSource || !foundLayer )
6496 {
6497 return QVariant();
6498 }
6500 QString cacheValueKey;
6501 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6502 {
6503 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6504
6505 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6506 QString filterString;
6507 for ( ; i != attributeMap.constEnd(); ++i )
6508 {
6509 if ( !filterString.isEmpty() )
6510 {
6511 filterString.append( " AND " );
6512 }
6513 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6514 }
6515 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6516 if ( context && context->hasCachedValue( cacheValueKey ) )
6517 {
6518 return context->cachedValue( cacheValueKey );
6519 }
6520 req.setFilterExpression( filterString );
6521 }
6522 else
6523 {
6524 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6525 int attributeId = featureSource->fields().lookupField( attribute );
6526 if ( attributeId == -1 )
6527 {
6528 return QVariant();
6529 }
6530
6531 const QVariant &attVal = values.at( 2 );
6532
6533 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6534 if ( context && context->hasCachedValue( cacheValueKey ) )
6535 {
6536 return context->cachedValue( cacheValueKey );
6537 }
6538
6540 }
6541 req.setLimit( 1 );
6542 req.setTimeout( 10000 );
6543 req.setRequestMayBeNested( true );
6544 if ( context )
6545 req.setFeedback( context->feedback() );
6546 if ( !parent->needsGeometry() )
6547 {
6549 }
6550 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6551
6552 QgsFeature fet;
6553 QVariant res;
6554 if ( fIt.nextFeature( fet ) )
6555 {
6556 res = QVariant::fromValue( fet );
6557 }
6558
6559 if ( context )
6560 context->setCachedValue( cacheValueKey, res );
6561 return res;
6562}
6563
6564static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6565{
6566 QVariant result;
6567 QString fieldName;
6568
6569 if ( context )
6570 {
6571 if ( !values.isEmpty() )
6572 {
6573 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6574 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6575 fieldName = col->name();
6576 else if ( values.size() == 2 )
6577 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6578 }
6579
6580 QVariant value = values.at( 0 );
6581
6582 const QgsFields fields = context->fields();
6583 int fieldIndex = fields.lookupField( fieldName );
6584
6585 if ( fieldIndex == -1 )
6586 {
6587 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6588 }
6589 else
6590 {
6591 // TODO this function is NOT thread safe
6593 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6595
6596 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6597 if ( context->hasCachedValue( cacheValueKey ) )
6598 {
6599 return context->cachedValue( cacheValueKey );
6600 }
6601
6602 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6604
6605 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6606
6607 QVariant cache;
6608 if ( !context->hasCachedValue( cacheKey ) )
6609 {
6610 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6611 context->setCachedValue( cacheKey, cache );
6612 }
6613 else
6614 cache = context->cachedValue( cacheKey );
6615
6616 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6617
6618 context->setCachedValue( cacheValueKey, result );
6619 }
6620 }
6621 else
6622 {
6623 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6624 }
6625
6626 return result;
6627}
6628
6629static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6630{
6631 const QVariant data = values.at( 0 );
6632 const QMimeDatabase db;
6633 return db.mimeTypeForData( data.toByteArray() ).name();
6634}
6635
6636static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6637{
6638 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6639
6640 bool foundLayer = false;
6641 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6642 {
6643 if ( !layer )
6644 return QVariant();
6645
6646 // here, we always prefer the layer metadata values over the older server-specific published values
6647 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6648 return layer->name();
6649 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6650 return layer->id();
6651 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6652 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
6653 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6654 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
6655 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6656 {
6657 QStringList keywords;
6658 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6659 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6660 {
6661 keywords.append( it.value() );
6662 }
6663 if ( !keywords.isEmpty() )
6664 return keywords;
6665 return layer->serverProperties()->keywordList();
6666 }
6667 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6668 return layer->serverProperties()->dataUrl();
6669 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6670 {
6671 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
6672 }
6673 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6674 return layer->serverProperties()->attributionUrl();
6675 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6676 return layer->publicSource();
6677 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6678 return layer->minimumScale();
6679 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6680 return layer->maximumScale();
6681 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6682 return layer->isEditable();
6683 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6684 return layer->crs().authid();
6685 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6686 return layer->crs().toProj();
6687 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6688 return layer->crs().description();
6689 else if ( QString::compare( layerProperty, QStringLiteral( "crs_ellipsoid" ), Qt::CaseInsensitive ) == 0 )
6690 return layer->crs().ellipsoidAcronym();
6691 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6692 {
6693 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6694 QVariant result = QVariant::fromValue( extentGeom );
6695 return result;
6696 }
6697 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6698 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6699 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6700 {
6701 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6702 return decodedUri.value( QStringLiteral( "path" ) );
6703 }
6704 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6705 {
6706 switch ( layer->type() )
6707 {
6709 return QCoreApplication::translate( "expressions", "Vector" );
6711 return QCoreApplication::translate( "expressions", "Raster" );
6713 return QCoreApplication::translate( "expressions", "Mesh" );
6715 return QCoreApplication::translate( "expressions", "Vector Tile" );
6717 return QCoreApplication::translate( "expressions", "Plugin" );
6719 return QCoreApplication::translate( "expressions", "Annotation" );
6721 return QCoreApplication::translate( "expressions", "Point Cloud" );
6723 return QCoreApplication::translate( "expressions", "Group" );
6725 return QCoreApplication::translate( "expressions", "Tiled Scene" );
6726 }
6727 }
6728 else
6729 {
6730 //vector layer methods
6731 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6732 if ( vLayer )
6733 {
6734 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6735 return vLayer->storageType();
6736 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6738 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6739 return QVariant::fromValue( vLayer->featureCount() );
6740 }
6741 }
6742
6743 return QVariant();
6744 }, foundLayer );
6745
6746 if ( !foundLayer )
6747 return QVariant();
6748 else
6749 return res;
6750}
6751
6752static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6753{
6754 const QString uriPart = values.at( 1 ).toString();
6755
6756 bool foundLayer = false;
6757
6758 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
6759 {
6760 if ( !layer->dataProvider() )
6761 {
6762 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
6763 return QVariant();
6764 }
6765
6766 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
6767
6768 if ( !uriPart.isNull() )
6769 {
6770 return decodedUri.value( uriPart );
6771 }
6772 else
6773 {
6774 return decodedUri;
6775 }
6776 }, foundLayer );
6777
6778 if ( !foundLayer )
6779 {
6780 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
6781 return QVariant();
6782 }
6783 else
6784 {
6785 return res;
6786 }
6787}
6788
6789static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6790{
6791 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6792 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6793
6794 bool foundLayer = false;
6795 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
6796 {
6797 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6798 if ( !rl )
6799 return QVariant();
6800
6801 if ( band < 1 || band > rl->bandCount() )
6802 {
6803 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
6804 return QVariant();
6805 }
6806
6808
6809 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6811 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6813 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6815 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6817 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6819 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6821 else
6822 {
6823 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6824 return QVariant();
6825 }
6826
6827 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6828 switch ( stat )
6829 {
6831 return stats.mean;
6833 return stats.stdDev;
6835 return stats.minimumValue;
6837 return stats.maximumValue;
6839 return stats.range;
6841 return stats.sum;
6842 default:
6843 break;
6844 }
6845 return QVariant();
6846 }, foundLayer );
6847
6848 if ( !foundLayer )
6849 {
6850#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
6851 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
6852#endif
6853 return QVariant();
6854 }
6855 else
6856 {
6857 return res;
6858 }
6859}
6860
6861static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6862{
6863 return values;
6864}
6865
6866static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6867{
6868 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6869 bool ascending = values.value( 1 ).toBool();
6870 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6871 return list;
6872}
6873
6874static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6875{
6876 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6877}
6878
6879static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6880{
6881 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6882}
6883
6884static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6885{
6886 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6887}
6888
6889static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6890{
6891 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6892 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6893 int match = 0;
6894 for ( const auto &item : listB )
6895 {
6896 if ( listA.contains( item ) )
6897 match++;
6898 }
6899
6900 return QVariant( match == listB.count() );
6901}
6902
6903static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6904{
6905 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
6906}
6907
6908static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6909{
6910 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6911 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6912 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
6913 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
6914 return list.at( list.length() + pos );
6915 return QVariant();
6916}
6917
6918static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6919{
6920 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6921 return list.value( 0 );
6922}
6923
6924static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6925{
6926 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6927 return list.value( list.size() - 1 );
6928}
6929
6930static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6931{
6932 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6933 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6934}
6935
6936static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6937{
6938 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6939 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6940}
6941
6942static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6943{
6944 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6945 int i = 0;
6946 double total = 0.0;
6947 for ( const QVariant &item : list )
6948 {
6949 switch ( item.userType() )
6950 {
6951 case QMetaType::Int:
6952 case QMetaType::UInt:
6953 case QMetaType::LongLong:
6954 case QMetaType::ULongLong:
6955 case QMetaType::Float:
6956 case QMetaType::Double:
6957 total += item.toDouble();
6958 ++i;
6959 break;
6960 }
6961 }
6962 return i == 0 ? QVariant() : total / i;
6963}
6964
6965static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6966{
6967 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6968 QVariantList numbers;
6969 for ( const auto &item : list )
6970 {
6971 switch ( item.userType() )
6972 {
6973 case QMetaType::Int:
6974 case QMetaType::UInt:
6975 case QMetaType::LongLong:
6976 case QMetaType::ULongLong:
6977 case QMetaType::Float:
6978 case QMetaType::Double:
6979 numbers.append( item );
6980 break;
6981 }
6982 }
6983 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6984 const int count = numbers.count();
6985 if ( count == 0 )
6986 {
6987 return QVariant();
6988 }
6989 else if ( count % 2 )
6990 {
6991 return numbers.at( count / 2 );
6992 }
6993 else
6994 {
6995 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
6996 }
6997}
6998
6999static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7000{
7001 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7002 int i = 0;
7003 double total = 0.0;
7004 for ( const QVariant &item : list )
7005 {
7006 switch ( item.userType() )
7007 {
7008 case QMetaType::Int:
7009 case QMetaType::UInt:
7010 case QMetaType::LongLong:
7011 case QMetaType::ULongLong:
7012 case QMetaType::Float:
7013 case QMetaType::Double:
7014 total += item.toDouble();
7015 ++i;
7016 break;
7017 }
7018 }
7019 return i == 0 ? QVariant() : total;
7020}
7021
7022static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7023{
7024 QVariant result = value;
7025 result.convert( static_cast<int>( type ) );
7026 return result;
7027}
7028
7029static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7030{
7031 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7032 QHash< QVariant, int > hash;
7033 for ( const auto &item : list )
7034 {
7035 ++hash[item];
7036 }
7037 const QList< int > occurrences = hash.values();
7038 if ( occurrences.empty() )
7039 return QVariantList();
7040
7041 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7042
7043 const QString option = values.at( 1 ).toString();
7044 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7045 {
7046 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7047 }
7048 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7049 {
7050 if ( hash.isEmpty() )
7051 return QVariant();
7052
7053 return QVariant( hash.key( maxValue ) );
7054 }
7055 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7056 {
7057 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7058 }
7059 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
7060 {
7061 if ( maxValue * 2 <= list.size() )
7062 return QVariant();
7063
7064 return QVariant( hash.key( maxValue ) );
7065 }
7066 else
7067 {
7068 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7069 return QVariant();
7070 }
7071}
7072
7073static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7074{
7075 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7076 QHash< QVariant, int > hash;
7077 for ( const auto &item : list )
7078 {
7079 ++hash[item];
7080 }
7081 const QList< int > occurrences = hash.values();
7082 if ( occurrences.empty() )
7083 return QVariantList();
7084
7085 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7086
7087 const QString option = values.at( 1 ).toString();
7088 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7089 {
7090 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7091 }
7092 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7093 {
7094 if ( hash.isEmpty() )
7095 return QVariant();
7096
7097 return QVariant( hash.key( minValue ) );
7098 }
7099 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7100 {
7101 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7102 }
7103 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
7104 {
7105 if ( hash.isEmpty() )
7106 return QVariant();
7107
7108 // Remove the majority, all others are minority
7109 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7110 if ( maxValue * 2 > list.size() )
7111 hash.remove( hash.key( maxValue ) );
7112
7113 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7114 }
7115 else
7116 {
7117 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7118 return QVariant();
7119 }
7120}
7121
7122static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7123{
7124 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7125 list.append( values.at( 1 ) );
7126 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7127}
7128
7129static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7130{
7131 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7132 list.prepend( values.at( 1 ) );
7133 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7134}
7135
7136static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7137{
7138 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7139 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7140 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7141}
7142
7143static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7144{
7145 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7146 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7147 if ( position < 0 )
7148 position = position + list.length();
7149 if ( position >= 0 && position < list.length() )
7150 list.removeAt( position );
7151 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7152}
7153
7154static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7155{
7156 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7157 return QVariant();
7158
7159 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7160
7161 const QVariant toRemove = values.at( 1 );
7162 if ( QgsVariantUtils::isNull( toRemove ) )
7163 {
7164 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7165 {
7166 return QgsVariantUtils::isNull( element );
7167 } ), list.end() );
7168 }
7169 else
7170 {
7171 list.removeAll( toRemove );
7172 }
7173 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7174}
7175
7176static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7177{
7178 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7179 {
7180 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7181
7182 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7183 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7184 {
7185 int index = list.indexOf( it.key() );
7186 while ( index >= 0 )
7187 {
7188 list.replace( index, it.value() );
7189 index = list.indexOf( it.key() );
7190 }
7191 }
7192
7193 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7194 }
7195 else if ( values.count() == 3 )
7196 {
7197 QVariantList before;
7198 QVariantList after;
7199 bool isSingleReplacement = false;
7200
7201 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7202 {
7203 before = QVariantList() << values.at( 1 );
7204 }
7205 else
7206 {
7207 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7208 }
7209
7210 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7211 {
7212 after = QVariantList() << values.at( 2 );
7213 isSingleReplacement = true;
7214 }
7215 else
7216 {
7217 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7218 }
7219
7220 if ( !isSingleReplacement && before.length() != after.length() )
7221 {
7222 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7223 return QVariant();
7224 }
7225
7226 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7227 for ( int i = 0; i < before.length(); i++ )
7228 {
7229 int index = list.indexOf( before.at( i ) );
7230 while ( index >= 0 )
7231 {
7232 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7233 index = list.indexOf( before.at( i ) );
7234 }
7235 }
7236
7237 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7238 }
7239 else
7240 {
7241 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7242 return QVariant();
7243 }
7244}
7245
7246static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7247{
7248 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7249 QVariantList list_new;
7250
7251 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7252 {
7253 while ( list.removeOne( cur ) )
7254 {
7255 list_new.append( cur );
7256 }
7257 }
7258
7259 list_new.append( list );
7260
7261 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7262}
7263
7264static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7265{
7266 QVariantList list;
7267 for ( const QVariant &cur : values )
7268 {
7269 list += QgsExpressionUtils::getListValue( cur, parent );
7270 }
7271 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7272}
7273
7274static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7275{
7276 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7277 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7278 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7279 int slice_length = 0;
7280 // negative positions means positions taken relative to the end of the array
7281 if ( start_pos < 0 )
7282 {
7283 start_pos = list.length() + start_pos;
7284 }
7285 if ( end_pos >= 0 )
7286 {
7287 slice_length = end_pos - start_pos + 1;
7288 }
7289 else
7290 {
7291 slice_length = list.length() + end_pos - start_pos + 1;
7292 }
7293 //avoid negative lengths in QList.mid function
7294 if ( slice_length < 0 )
7295 {
7296 slice_length = 0;
7297 }
7298 list = list.mid( start_pos, slice_length );
7299 return list;
7300}
7301
7302static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7303{
7304 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7305 std::reverse( list.begin(), list.end() );
7306 return list;
7307}
7308
7309static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7310{
7311 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7312 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7313 for ( const QVariant &cur : array2 )
7314 {
7315 if ( array1.contains( cur ) )
7316 return QVariant( true );
7317 }
7318 return QVariant( false );
7319}
7320
7321static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7322{
7323 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7324
7325 QVariantList distinct;
7326
7327 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7328 {
7329 if ( !distinct.contains( *it ) )
7330 {
7331 distinct += ( *it );
7332 }
7333 }
7334
7335 return distinct;
7336}
7337
7338static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7339{
7340 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7341 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7342 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7343
7344 QString str;
7345
7346 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7347 {
7348 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7349 if ( it != ( array.constEnd() - 1 ) )
7350 {
7351 str += delimiter;
7352 }
7353 }
7354
7355 return QVariant( str );
7356}
7357
7358static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7359{
7360 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7361 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7362 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7363
7364 QStringList list = str.split( delimiter );
7365 QVariantList array;
7366
7367 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7368 {
7369 array += ( !( *it ).isEmpty() ) ? *it : empty;
7370 }
7371
7372 return array;
7373}
7374
7375static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7376{
7377 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7378 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7379 if ( document.isNull() )
7380 return QVariant();
7381
7382 return document.toVariant();
7383}
7384
7385static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7386{
7387 Q_UNUSED( parent )
7388 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7389 return QString( document.toJson( QJsonDocument::Compact ) );
7390}
7391
7392static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7393{
7394 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7395 if ( str.isEmpty() )
7396 return QVariantMap();
7397 str = str.trimmed();
7398
7399 return QgsHstoreUtils::parse( str );
7400}
7401
7402static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7403{
7404 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7405 return QgsHstoreUtils::build( map );
7406}
7407
7408static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7409{
7410 QVariantMap result;
7411 for ( int i = 0; i + 1 < values.length(); i += 2 )
7412 {
7413 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7414 }
7415 return result;
7416}
7417
7418static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7419{
7420 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7421 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7422 QVariantMap resultMap;
7423
7424 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7425 {
7426 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7427 }
7428
7429 return resultMap;
7430}
7431
7432static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7433{
7434 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7435}
7436
7437static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7438{
7439 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7440}
7441
7442static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7443{
7444 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7445 map.remove( values.at( 1 ).toString() );
7446 return map;
7447}
7448
7449static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7450{
7451 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7452 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7453 return map;
7454}
7455
7456static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7457{
7458 QVariantMap result;
7459 for ( const QVariant &cur : values )
7460 {
7461 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7462 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7463 result.insert( it.key(), it.value() );
7464 }
7465 return result;
7466}
7467
7468static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7469{
7470 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7471}
7472
7473static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7474{
7475 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7476}
7477
7478static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7479{
7480 const QString envVarName = values.at( 0 ).toString();
7481 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7482 return QVariant();
7483
7484 return QProcessEnvironment::systemEnvironment().value( envVarName );
7485}
7486
7487static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7488{
7489 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7490 if ( parent->hasEvalError() )
7491 {
7492 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7493 return QVariant();
7494 }
7495 return QFileInfo( file ).completeBaseName();
7496}
7497
7498static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7499{
7500 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7501 if ( parent->hasEvalError() )
7502 {
7503 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7504 return QVariant();
7505 }
7506 return QFileInfo( file ).completeSuffix();
7507}
7508
7509static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7510{
7511 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7512 if ( parent->hasEvalError() )
7513 {
7514 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7515 return QVariant();
7516 }
7517 return QFileInfo::exists( file );
7518}
7519
7520static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7521{
7522 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7523 if ( parent->hasEvalError() )
7524 {
7525 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7526 return QVariant();
7527 }
7528 return QFileInfo( file ).fileName();
7529}
7530
7531static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7532{
7533 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7534 if ( parent->hasEvalError() )
7535 {
7536 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7537 return QVariant();
7538 }
7539 return QFileInfo( file ).isFile();
7540}
7541
7542static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7543{
7544 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7545 if ( parent->hasEvalError() )
7546 {
7547 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7548 return QVariant();
7549 }
7550 return QFileInfo( file ).isDir();
7551}
7552
7553static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7554{
7555 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7556 if ( parent->hasEvalError() )
7557 {
7558 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7559 return QVariant();
7560 }
7561 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7562}
7563
7564static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7565{
7566 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7567 if ( parent->hasEvalError() )
7568 {
7569 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7570 return QVariant();
7571 }
7572 return QFileInfo( file ).size();
7573}
7574
7575static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7576{
7577 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7578}
7579
7580static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7581{
7582 QVariant hash;
7583 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7584 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7585
7586 if ( method == QLatin1String( "md4" ) )
7587 {
7588 hash = fcnHash( str, QCryptographicHash::Md4 );
7589 }
7590 else if ( method == QLatin1String( "md5" ) )
7591 {
7592 hash = fcnHash( str, QCryptographicHash::Md5 );
7593 }
7594 else if ( method == QLatin1String( "sha1" ) )
7595 {
7596 hash = fcnHash( str, QCryptographicHash::Sha1 );
7597 }
7598 else if ( method == QLatin1String( "sha224" ) )
7599 {
7600 hash = fcnHash( str, QCryptographicHash::Sha224 );
7601 }
7602 else if ( method == QLatin1String( "sha256" ) )
7603 {
7604 hash = fcnHash( str, QCryptographicHash::Sha256 );
7605 }
7606 else if ( method == QLatin1String( "sha384" ) )
7607 {
7608 hash = fcnHash( str, QCryptographicHash::Sha384 );
7609 }
7610 else if ( method == QLatin1String( "sha512" ) )
7611 {
7612 hash = fcnHash( str, QCryptographicHash::Sha512 );
7613 }
7614 else if ( method == QLatin1String( "sha3_224" ) )
7615 {
7616 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7617 }
7618 else if ( method == QLatin1String( "sha3_256" ) )
7619 {
7620 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7621 }
7622 else if ( method == QLatin1String( "sha3_384" ) )
7623 {
7624 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7625 }
7626 else if ( method == QLatin1String( "sha3_512" ) )
7627 {
7628 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7629 }
7630 else if ( method == QLatin1String( "keccak_224" ) )
7631 {
7632 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7633 }
7634 else if ( method == QLatin1String( "keccak_256" ) )
7635 {
7636 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7637 }
7638 else if ( method == QLatin1String( "keccak_384" ) )
7639 {
7640 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7641 }
7642 else if ( method == QLatin1String( "keccak_512" ) )
7643 {
7644 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7645 }
7646 else
7647 {
7648 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7649 }
7650 return hash;
7651}
7652
7653static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7654{
7655 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7656}
7657
7658static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7659{
7660 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7661}
7662
7663static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7664{
7665 const QByteArray input = values.at( 0 ).toByteArray();
7666 return QVariant( QString( input.toBase64() ) );
7667}
7668
7669static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7670{
7671 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7672 QUrlQuery query;
7673 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7674 {
7675 query.addQueryItem( it.key(), it.value().toString() );
7676 }
7677 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7678}
7679
7680static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7681{
7682 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7683 const QByteArray base64 = value.toLocal8Bit();
7684 const QByteArray decoded = QByteArray::fromBase64( base64 );
7685 return QVariant( decoded );
7686}
7687
7688typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7689
7690static 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 )
7691{
7692
7693 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7694 // TODO this function is NOT thread safe
7696 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7698
7699 QgsFeatureRequest request;
7700 request.setTimeout( 10000 );
7701 request.setRequestMayBeNested( true );
7702 request.setFeedback( context->feedback() );
7703
7704 // First parameter is the overlay layer
7705 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7707
7708 const bool layerCanBeCached = node->isStatic( parent, context );
7709 QVariant targetLayerValue = node->eval( parent, context );
7711
7712 // Second parameter is the expression to evaluate (or null for testonly)
7713 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7715 QString subExpString = node->dump();
7716
7717 bool testOnly = ( subExpString == "NULL" );
7718 // TODO this function is NOT thread safe
7720 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7722 if ( !targetLayer ) // No layer, no joy
7723 {
7724 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7725 return QVariant();
7726 }
7727
7728 // Third parameter is the filtering expression
7729 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7731 QString filterString = node->dump();
7732 if ( filterString != "NULL" )
7733 {
7734 request.setFilterExpression( filterString ); //filter cached features
7735 }
7736
7737 // Fourth parameter is the limit
7738 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7740 QVariant limitValue = node->eval( parent, context );
7742 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7743
7744 // Fifth parameter (for nearest only) is the max distance
7745 double max_distance = 0;
7746 if ( isNearestFunc ) //maxdistance param handling
7747 {
7748 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7750 QVariant distanceValue = node->eval( parent, context );
7752 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
7753 }
7754
7755 // Fifth or sixth (for nearest only) parameter is the cache toggle
7756 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
7758 QVariant cacheValue = node->eval( parent, context );
7760 bool cacheEnabled = cacheValue.toBool();
7761
7762 // Sixth parameter (for intersects only) is the min overlap (area or length)
7763 // Seventh parameter (for intersects only) is the min inscribed circle radius
7764 // Eighth parameter (for intersects only) is the return_details
7765 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
7766 double minOverlap { -1 };
7767 double minInscribedCircleRadius { -1 };
7768 bool returnDetails = false; //#spellok
7769 bool sortByMeasure = false;
7770 bool sortAscending = false;
7771 bool requireMeasures = false;
7772 bool overlapOrRadiusFilter = false;
7773 if ( isIntersectsFunc )
7774 {
7775
7776 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7778 const QVariant minOverlapValue = node->eval( parent, context );
7780 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
7781 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7783 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
7785 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
7786 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
7787 // Return measures is only effective when an expression is set
7788 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
7789 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
7790 // Sort by measures is only effective when an expression is set
7791 const QString sorting { node->eval( parent, context ).toString().toLower() };
7792 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
7793 sortAscending = sorting.startsWith( "asc" );
7794 requireMeasures = sortByMeasure || returnDetails; //#spellok
7795 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
7796 }
7797
7798
7799 FEAT_FROM_CONTEXT( context, feat )
7800 const QgsGeometry geometry = feat.geometry();
7801
7802 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7803 {
7804 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7805 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7806 }
7807
7808 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7809
7810 QgsRectangle intDomain = geometry.boundingBox();
7811 if ( bboxGrow != 0 )
7812 {
7813 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7814 }
7815
7816 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7817
7818 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7819 // Otherwise, it can be toggled by the user
7820 QgsSpatialIndex spatialIndex;
7821 QgsVectorLayer *cachedTarget;
7822 QList<QgsFeature> features;
7823 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7824 {
7825 // If the cache (local spatial index) is enabled, we materialize the whole
7826 // layer, then do the request on that layer instead.
7827 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7828 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7829
7830 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7831 {
7832 cachedTarget = targetLayer->materialize( request );
7833 if ( layerCanBeCached )
7834 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7835 }
7836 else
7837 {
7838 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7839 }
7840
7841 if ( !context->hasCachedValue( cacheIndex ) )
7842 {
7843 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7844 if ( layerCanBeCached )
7845 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7846 }
7847 else
7848 {
7849 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7850 }
7851
7852 QList<QgsFeatureId> fidsList;
7853 if ( isNearestFunc )
7854 {
7855 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7856 }
7857 else
7858 {
7859 fidsList = spatialIndex.intersects( intDomain );
7860 }
7861
7862 QListIterator<QgsFeatureId> i( fidsList );
7863 while ( i.hasNext() )
7864 {
7865 QgsFeatureId fId2 = i.next();
7866 if ( sameLayers && feat.id() == fId2 )
7867 continue;
7868 features.append( cachedTarget->getFeature( fId2 ) );
7869 }
7870
7871 }
7872 else
7873 {
7874 // If the cache (local spatial index) is not enabled, we directly
7875 // get the features from the target layer
7876 request.setFilterRect( intDomain );
7877 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7878 QgsFeature feat2;
7879 while ( fit.nextFeature( feat2 ) )
7880 {
7881 if ( sameLayers && feat.id() == feat2.id() )
7882 continue;
7883 features.append( feat2 );
7884 }
7885 }
7886
7887 QgsExpression subExpression;
7888 QgsExpressionContext subContext;
7889 if ( !testOnly )
7890 {
7891 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7892 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7893
7894 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7895 {
7896 subExpression = QgsExpression( subExpString );
7898 subExpression.prepare( &subContext );
7899 }
7900 else
7901 {
7902 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
7903 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
7904 }
7905 }
7906
7907 // //////////////////////////////////////////////////////////////////
7908 // Helper functions for geometry tests
7909
7910 // Test function for linestring geometries, returns TRUE if test passes
7911 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
7912 {
7913 bool testResult { false };
7914 // For return measures:
7915 QVector<double> overlapValues;
7916 for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
7917 {
7918 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
7919 // Check min overlap for intersection (if set)
7920 if ( minOverlap != -1 || requireMeasures )
7921 {
7922 overlapValue = geom->length();
7923 overlapValues.append( overlapValue );
7924 if ( minOverlap != -1 )
7925 {
7926 if ( overlapValue >= minOverlap )
7927 {
7928 testResult = true;
7929 }
7930 else
7931 {
7932 continue;
7933 }
7934 }
7935 }
7936 }
7937
7938 if ( ! overlapValues.isEmpty() )
7939 {
7940 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7941 }
7942
7943 return testResult;
7944 };
7945
7946 // Test function for polygon geometries, returns TRUE if test passes
7947 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
7948 {
7949 // overlap and inscribed circle tests must be checked both (if the values are != -1)
7950 bool testResult { false };
7951 // For return measures:
7952 QVector<double> overlapValues;
7953 QVector<double> radiusValues;
7954 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
7955 {
7956 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
7957 // Check min overlap for intersection (if set)
7958 if ( minOverlap != -1 || requireMeasures )
7959 {
7960 overlapValue = geom->area();
7961 overlapValues.append( geom->area() );
7962 if ( minOverlap != - 1 )
7963 {
7964 if ( overlapValue >= minOverlap )
7965 {
7966 testResult = true;
7967 }
7968 else
7969 {
7970 continue;
7971 }
7972 }
7973 }
7974
7975 // Check min inscribed circle radius for intersection (if set)
7976 if ( minInscribedCircleRadius != -1 || requireMeasures )
7977 {
7978 const QgsRectangle bbox = geom->boundingBox();
7979 const double width = bbox.width();
7980 const double height = bbox.height();
7981 const double size = width > height ? width : height;
7982 const double tolerance = size / 100.0;
7983 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
7984 testResult = radiusValue >= minInscribedCircleRadius;
7985 radiusValues.append( radiusValues );
7986 }
7987 } // end for parts
7988
7989 // Get the max values
7990 if ( !radiusValues.isEmpty() )
7991 {
7992 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
7993 }
7994
7995 if ( ! overlapValues.isEmpty() )
7996 {
7997 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7998 }
7999
8000 return testResult;
8001
8002 };
8003
8004
8005 bool found = false;
8006 int foundCount = 0;
8007 QVariantList results;
8008
8009 QListIterator<QgsFeature> i( features );
8010 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8011 {
8012
8013 QgsFeature feat2 = i.next();
8014
8015
8016 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8017 {
8018
8019 double overlapValue = -1;
8020 double radiusValue = -1;
8021
8022 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8023 {
8024 const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8025
8026 // Depending on the intersection geometry type and on the geometry type of
8027 // the tested geometry we can run different tests and collect different measures
8028 // that can be used for sorting (if required).
8029 switch ( intersection.type() )
8030 {
8031
8033 {
8034
8035 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8036 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8037
8038 if ( ! testResult && overlapOrRadiusFilter )
8039 {
8040 continue;
8041 }
8042
8043 break;
8044 }
8045
8047 {
8048
8049 // If the intersection is a linestring and a minimum circle is required
8050 // we can discard this result immediately.
8051 if ( minInscribedCircleRadius != -1 )
8052 {
8053 continue;
8054 }
8055
8056 // Otherwise a test for the overlap value is performed.
8057 const bool testResult { testLinestring( intersection, overlapValue ) };
8058
8059 if ( ! testResult && overlapOrRadiusFilter )
8060 {
8061 continue;
8062 }
8063
8064 break;
8065 }
8066
8068 {
8069
8070 // If the intersection is a point and a minimum circle is required
8071 // we can discard this result immediately.
8072 if ( minInscribedCircleRadius != -1 )
8073 {
8074 continue;
8075 }
8076
8077 bool testResult { false };
8078 if ( minOverlap != -1 || requireMeasures )
8079 {
8080 // Initially set this to 0 because it's a point intersection...
8081 overlapValue = 0;
8082 // ... but if the target geometry is not a point and the source
8083 // geometry is a point, we must record the length or the area
8084 // of the intersected geometry and use that as a measure for
8085 // sorting or reporting.
8086 if ( geometry.type() == Qgis::GeometryType::Point )
8087 {
8088 switch ( feat2.geometry().type() )
8089 {
8093 {
8094 break;
8095 }
8097 {
8098 testResult = testLinestring( feat2.geometry(), overlapValue );
8099 break;
8100 }
8102 {
8103 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8104 break;
8105 }
8106 }
8107 }
8108
8109 if ( ! testResult && overlapOrRadiusFilter )
8110 {
8111 continue;
8112 }
8113
8114 }
8115 break;
8116 }
8117
8120 {
8121 continue;
8122 }
8123 }
8124 }
8125
8126 found = true;
8127 foundCount++;
8128
8129 // We just want a single boolean result if there is any intersect: finish and return true
8130 if ( testOnly )
8131 break;
8132
8133 if ( !invert )
8134 {
8135 // We want a list of attributes / geometries / other expression values, evaluate now
8136 subContext.setFeature( feat2 );
8137 const QVariant expResult = subExpression.evaluate( &subContext );
8138
8139 if ( requireMeasures )
8140 {
8141 QVariantMap resultRecord;
8142 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
8143 resultRecord.insert( QStringLiteral( "result" ), expResult );
8144 // Overlap is always added because return measures was set
8145 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
8146 // Radius is only added when is different than -1 (because for linestrings is not set)
8147 if ( radiusValue != -1 )
8148 {
8149 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
8150 }
8151 results.append( resultRecord );
8152 }
8153 else
8154 {
8155 results.append( expResult );
8156 }
8157 }
8158 else
8159 {
8160 // If not, results is a list of found ids, which we'll inverse and evaluate below
8161 results.append( feat2.id() );
8162 }
8163 }
8164 }
8165
8166 if ( testOnly )
8167 {
8168 if ( invert )
8169 found = !found;//for disjoint condition
8170 return found;
8171 }
8172
8173 if ( !invert )
8174 {
8175 if ( requireMeasures )
8176 {
8177 if ( sortByMeasure )
8178 {
8179 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8180 {
8181 return sortAscending ?
8182 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
8183 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
8184 } );
8185 }
8186 // Resize
8187 if ( limit > 0 && results.size() > limit )
8188 {
8189 results.erase( results.begin() + limit );
8190 }
8191
8192 if ( ! returnDetails ) //#spellok
8193 {
8194 QVariantList expResults;
8195 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8196 {
8197 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
8198 }
8199 return expResults;
8200 }
8201 }
8202
8203 return results;
8204 }
8205
8206 // for disjoint condition returns the results for cached layers not intersected feats
8207 QVariantList disjoint_results;
8208 QgsFeature feat2;
8209 QgsFeatureRequest request2;
8210 request2.setLimit( limit );
8211 if ( context )
8212 request2.setFeedback( context->feedback() );
8213 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8214 while ( fi.nextFeature( feat2 ) )
8215 {
8216 if ( !results.contains( feat2.id() ) )
8217 {
8218 subContext.setFeature( feat2 );
8219 disjoint_results.append( subExpression.evaluate( &subContext ) );
8220 }
8221 }
8222 return disjoint_results;
8223
8224}
8225
8226// Intersect functions:
8227
8228static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8229{
8230 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8231}
8232
8233static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8234{
8235 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8236}
8237
8238static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8239{
8240 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8241}
8242
8243static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8244{
8245 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8246}
8247
8248static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8249{
8250 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8251}
8252
8253static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8254{
8255 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8256}
8258static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8259{
8260 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8261}
8262
8263static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8264{
8265 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8266}
8267
8268const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8269{
8270 // The construction of the list isn't thread-safe, and without the mutex,
8271 // crashes in the WFS provider may occur, since it can parse expressions
8272 // in parallel.
8273 // The mutex needs to be recursive.
8274 QMutexLocker locker( &sFunctionsMutex );
8275
8276 QList<QgsExpressionFunction *> &functions = *sFunctions();
8277
8278 if ( functions.isEmpty() )
8279 {
8281 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8282 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
8283 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
8284
8285 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8286 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8287 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8288
8289 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8290 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8291
8292 functions
8293 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
8294 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
8295 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
8296 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
8297 << new QgsStaticExpressionFunction( QStringLiteral( "bearing" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "source_crs" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "ellipsoid" ), true, QVariant() ), fcnBearing, QStringLiteral( "GeometryGroup" ) )
8298 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
8299 << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI_2 ), fcnProject, QStringLiteral( "GeometryGroup" ) )
8300 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
8301 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
8302 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
8303 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
8304 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
8305 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
8306 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
8307 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
8308 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
8309 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
8310 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
8311 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
8312 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
8313
8314 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
8315 randFunc->setIsStatic( false );
8316 functions << randFunc;
8317
8318 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRndF, QStringLiteral( "Math" ) );
8319 randfFunc->setIsStatic( false );
8320 functions << randfFunc;
8321
8322 functions
8323 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8324 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8325 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
8326 << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ), fcnLinearScale, QStringLiteral( "Math" ) )
8327 << new QgsStaticExpressionFunction( QStringLiteral( "scale_polynomial" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnPolynomialScale, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "scale_exp" ) )
8328 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exponential" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnExponentialScale, QStringLiteral( "Math" ) )
8329 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8330 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8331 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8332 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8333 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8334 << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tostring" ) )
8335 << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todatetime" ) )
8336 << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todate" ) )
8337 << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "totime" ) )
8338 << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tointerval" ) )
8339 << new QgsStaticExpressionFunction( QStringLiteral( "to_dm" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinute, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todm" ) )
8340 << new QgsStaticExpressionFunction( QStringLiteral( "to_dms" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinuteSecond, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todms" ) )
8341 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8342 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8343 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8344 << new QgsStaticExpressionFunction( QStringLiteral( "if" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "condition" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_true" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_false" ) ), fcnIf, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8345 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8346
8347 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8349 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8350 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8351 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8352 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8353 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8354 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8355 fcnAggregate,
8356 QStringLiteral( "Aggregates" ),
8357 QString(),
8358 []( const QgsExpressionNodeFunction * node )
8359 {
8360 // usesGeometry callback: return true if @parent variable is referenced
8361
8362 if ( !node )
8363 return true;
8364
8365 if ( !node->args() )
8366 return false;
8367
8368 QSet<QString> referencedVars;
8369 if ( node->args()->count() > 2 )
8370 {
8371 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8372 referencedVars = subExpressionNode->referencedVariables();
8373 }
8374
8375 if ( node->args()->count() > 3 )
8376 {
8377 QgsExpressionNode *filterNode = node->args()->at( 3 );
8378 referencedVars.unite( filterNode->referencedVariables() );
8379 }
8380 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8381 },
8382 []( const QgsExpressionNodeFunction * node )
8383 {
8384 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8385
8386 if ( !node )
8387 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8388
8389 if ( !node->args() )
8390 return QSet<QString>();
8391
8392 QSet<QString> referencedCols;
8393 QSet<QString> referencedVars;
8394
8395 if ( node->args()->count() > 2 )
8396 {
8397 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8398 referencedVars = subExpressionNode->referencedVariables();
8399 referencedCols = subExpressionNode->referencedColumns();
8400 }
8401 if ( node->args()->count() > 3 )
8402 {
8403 QgsExpressionNode *filterNode = node->args()->at( 3 );
8404 referencedVars = filterNode->referencedVariables();
8405 referencedCols.unite( filterNode->referencedColumns() );
8406 }
8407
8408 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8409 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8410 else
8411 return referencedCols;
8412 },
8413 true
8414 )
8415
8416 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8417 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8418 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8419 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8420 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8421 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8422 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8423
8424 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8425 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8426 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8427 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8428 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8429 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8430 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8431 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8432 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8433 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8434 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8435 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8436 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8437 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8438 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8439 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8440 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8441 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8442 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8443 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8444 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8445
8446 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8447 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8448
8449 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8450 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8451 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8452 fcnAge, QStringLiteral( "Date and Time" ) )
8453 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8454 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8455 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8456 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8457 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8458 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8459 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8460 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8461 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8462 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8463 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8464 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8465 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8466 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8467 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8468 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8469 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8470 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8471 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8472 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8473 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8474 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8475 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8476 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8477 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8478 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8479 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8480 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8481 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8482 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8483 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8484 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8485 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8486 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8487 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8488 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8489 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8490 << new QgsStaticExpressionFunction( QStringLiteral( "ltrim" ), QgsExpressionFunction::ParameterList()
8491 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8492 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnLTrim, QStringLiteral( "String" ) )
8493 << new QgsStaticExpressionFunction( QStringLiteral( "rtrim" ), QgsExpressionFunction::ParameterList()
8494 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8495 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnRTrim, QStringLiteral( "String" ) )
8496 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8497 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8498 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8499 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8500 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8501 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8502 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8503 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8504 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8505 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8506 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8507 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8508 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8509 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8510 false, QSet< QString >(), false, QStringList(), true )
8511 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8512 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8513 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8514 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8515 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8516 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8517 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8518 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8519 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8520 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8521 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8522 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8523 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8524 << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
8525 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8526 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8527 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8528 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8529 fcnColorMixRgb, QStringLiteral( "Color" ) )
8530 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8531 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8532 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8533 fcnColorMix, QStringLiteral( "Color" ) )
8534 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8535 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8536 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8537 fcnColorRgb, QStringLiteral( "Color" ) )
8538 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgbf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8539 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8540 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8541 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8542 fcnColorRgbF, QStringLiteral( "Color" ) )
8543 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8544 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8545 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8546 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8547 fncColorRgba, QStringLiteral( "Color" ) )
8548 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8549 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8550 fcnRampColor, QStringLiteral( "Color" ) )
8551 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8552 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8553 fcnCreateRamp, QStringLiteral( "Color" ) )
8554 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8555 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8556 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8557 fcnColorHsl, QStringLiteral( "Color" ) )
8558 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8559 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8560 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8561 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8562 fncColorHsla, QStringLiteral( "Color" ) )
8563 << new QgsStaticExpressionFunction( QStringLiteral( "color_hslf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8564 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8565 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8566 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8567 fcnColorHslF, QStringLiteral( "Color" ) )
8568 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8569 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8570 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8571 fcnColorHsv, QStringLiteral( "Color" ) )
8572 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8573 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8574 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8575 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8576 fncColorHsva, QStringLiteral( "Color" ) )
8577 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsvf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8578 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8579 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8580 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8581 fcnColorHsvF, QStringLiteral( "Color" ) )
8582 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8583 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8584 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8585 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8586 fcnColorCmyk, QStringLiteral( "Color" ) )
8587 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8588 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8589 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8590 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8591 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8592 fncColorCmyka, QStringLiteral( "Color" ) )
8593 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmykf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8594 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8595 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8596 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8597 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8598 fcnColorCmykF, QStringLiteral( "Color" ) )
8599 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8600 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8601 fncColorPart, QStringLiteral( "Color" ) )
8602 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8603 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8604 fncDarker, QStringLiteral( "Color" ) )
8605 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8606 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8607 fncLighter, QStringLiteral( "Color" ) )
8608 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8609
8610 // file info
8611 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8612 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8613 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8614 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8615 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8616 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8617 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8618 fcnFileName, QStringLiteral( "Files and Paths" ) )
8619 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8620 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8621 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8622 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8623 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8624 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8625 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8626 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8627
8628 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8629 fcnExif, QStringLiteral( "Files and Paths" ) )
8630 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8631 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8632
8633 // hash
8634 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8635 fcnGenericHash, QStringLiteral( "Conversions" ) )
8636 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8637 fcnHashMd5, QStringLiteral( "Conversions" ) )
8638 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8639 fcnHashSha256, QStringLiteral( "Conversions" ) )
8640
8641 //base64
8642 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8643 fcnToBase64, QStringLiteral( "Conversions" ) )
8644 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8645 fcnFromBase64, QStringLiteral( "Conversions" ) )
8646
8647 // deprecated stuff - hidden from users
8648 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8649
8650 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8651 geomFunc->setIsStatic( false );
8652 functions << geomFunc;
8653
8654 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8655 areaFunc->setIsStatic( false );
8656 functions << areaFunc;
8657
8658 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8659
8660 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8661 lengthFunc->setIsStatic( false );
8662 functions << lengthFunc;
8663
8664 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8665 perimeterFunc->setIsStatic( false );
8666 functions << perimeterFunc;
8667
8668 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8669
8670 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8672 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
8673
8674 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
8675 xFunc->setIsStatic( false );
8676 functions << xFunc;
8677
8678 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
8679 yFunc->setIsStatic( false );
8680 functions << yFunc;
8681
8682 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
8683 zFunc->setIsStatic( false );
8684 functions << zFunc;
8685
8686 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
8687 {
8688 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
8689 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
8690 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
8691 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
8692 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
8693 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
8694 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
8695 };
8696 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
8697 while ( i.hasNext() )
8698 {
8699 i.next();
8701 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8702 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8703 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8704 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
8705 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
8706 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
8707 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
8708 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
8709 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
8710 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8711
8712 // The current feature is accessed for the geometry, so this should not be cached
8713 fcnGeomOverlayFunc->setIsStatic( false );
8714 functions << fcnGeomOverlayFunc;
8715 }
8716
8717 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
8718 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8719 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8720 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8721 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
8722 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
8723 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
8724 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8725 // The current feature is accessed for the geometry, so this should not be cached
8726 fcnGeomOverlayNearestFunc->setIsStatic( false );
8727 functions << fcnGeomOverlayNearestFunc;
8728
8729 functions
8730 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
8731 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
8732 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
8733 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
8734 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
8735 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
8736 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
8737 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
8738 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8739 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
8740 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
8741 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
8742 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
8743 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
8744 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8745 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
8746 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
8747 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
8748 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
8749 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
8750 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8751 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8752 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
8753 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
8754 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
8755 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8756 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8757 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8758 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
8759 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
8760 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8761 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
8762 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
8763 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8764 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8765 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
8766 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
8767 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8768 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8769 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
8770 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
8771 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
8772 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
8773 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8774 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
8775 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
8776 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
8777 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8778 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8779 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
8780 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
8781 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
8782 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
8783 {
8784 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8785#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
8786 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
8787#else
8788 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
8789#endif
8790 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
8791 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
8792
8793 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
8794 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
8795 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
8796 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
8797
8798 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
8799 xAtFunc->setIsStatic( false );
8800 functions << xAtFunc;
8801
8802
8803 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
8804 yAtFunc->setIsStatic( false );
8805 functions << yAtFunc;
8806
8807 functions
8808 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
8809 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
8810 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
8811 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
8812 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
8813 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
8814 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8815 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
8816 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
8817 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
8818 << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ), fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "bbox" ) )
8819 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8820 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8821 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
8822 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8823 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8824 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
8825 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8826 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8827 fcnTouches, QStringLiteral( "GeometryGroup" ) )
8828 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8829 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8830 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
8831 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8832 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8833 fcnContains, QStringLiteral( "GeometryGroup" ) )
8834 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8835 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8836 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
8837 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8838 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8839 fcnWithin, QStringLiteral( "GeometryGroup" ) )
8840 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8841 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
8842 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
8843 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
8844 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8845 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
8846 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
8847 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
8848 fcnRotate, QStringLiteral( "GeometryGroup" ) )
8849 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8850 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
8851 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
8852 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
8853 fcnScale, QStringLiteral( "GeometryGroup" ) )
8854 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8855 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
8856 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
8857 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
8858 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
8859 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
8860 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
8861 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
8862 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
8863 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
8864 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
8865 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8866 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8867 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
8868 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
8869 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
8870 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
8871 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
8872 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8873 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
8874 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8875 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
8876 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8877 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
8878 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8879 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8880 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
8881 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
8882 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
8883 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8884 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
8885 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
8886 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8887 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
8888 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8889 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8890 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
8891 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8892 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8893 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8894 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8895 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8896 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
8897 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8898 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8899 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8900 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8901 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8902 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
8903 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8904 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
8905 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
8906 fcnExtend, QStringLiteral( "GeometryGroup" ) )
8907 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
8908 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
8909 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8910 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
8911 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
8912 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
8913 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8914 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8915 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
8916 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8917 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8918 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
8919 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
8920 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
8921 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
8922 {
8923 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
8924 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8925 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
8926 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
8927 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
8928 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
8929 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
8930 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
8931 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
8932 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
8933 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
8934 {
8935 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8936 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8937 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8938 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8939 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
8940 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
8941 {
8942 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8943 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8944 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8945 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8946 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8947 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8948 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8949 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
8950 {
8951 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8952 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8953 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8954 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8955 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
8956 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
8957 {
8958 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8959 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8960 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8961 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8962 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8963 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8964 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8965 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
8966 {
8967 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8968 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8969 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8970 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8971 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
8972 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
8973 {
8974 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8975 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8976 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8977 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8978 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8979 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8980 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8981 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
8982 {
8983 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8984 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
8985 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
8986 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
8987 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
8988 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
8989 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
8990 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
8991 {
8992 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8993 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
8994 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
8995 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
8996 {
8997 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8998 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8999 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
9000 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
9001 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
9002 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
9003 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
9004 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
9005 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
9006 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
9007 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
9008 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
9009 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9010 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
9011#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9012 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9013 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
9014 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
9015#endif
9016 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
9017 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9018 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
9019 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
9020 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9021 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
9022 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
9023 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9024 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9025 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
9026 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9027 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9028 fcnDifference, QStringLiteral( "GeometryGroup" ) )
9029 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9030 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9031 fcnDistance, QStringLiteral( "GeometryGroup" ) )
9032 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9033 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
9034 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
9035 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9036 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9037 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
9038 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9039 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9040 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
9041 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9042 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9043 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9044 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9045 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9046 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9047 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9048 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
9049 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
9050 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9051 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9052 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
9053 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9054 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
9055 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
9056 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
9057 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9058 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9059 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
9060 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
9061 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9062 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
9063 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9064 fcnZMax, QStringLiteral( "GeometryGroup" ) )
9065 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9066 fcnZMin, QStringLiteral( "GeometryGroup" ) )
9067 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9068 fcnMMax, QStringLiteral( "GeometryGroup" ) )
9069 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9070 fcnMMin, QStringLiteral( "GeometryGroup" ) )
9071 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9072 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
9073 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9074 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
9075
9076
9077 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9078 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
9079 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
9080 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
9081
9082 orderPartsFunc->setIsStaticFunction(
9083 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9084 {
9085 const QList< QgsExpressionNode *> argList = node->args()->list();
9086 for ( QgsExpressionNode *argNode : argList )
9087 {
9088 if ( !argNode->isStatic( parent, context ) )
9089 return false;
9090 }
9091
9092 if ( node->args()->count() > 1 )
9093 {
9094 QgsExpressionNode *argNode = node->args()->at( 1 );
9095
9096 QString expString = argNode->eval( parent, context ).toString();
9097
9098 QgsExpression e( expString );
9099
9100 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9101 return true;
9102 }
9103
9104 return true;
9105 } );
9106
9107 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9108 {
9109 if ( node->args()->count() > 1 )
9110 {
9111 QgsExpressionNode *argNode = node->args()->at( 1 );
9112 QString expression = argNode->eval( parent, context ).toString();
9114 e.prepare( context );
9115 context->setCachedValue( expression, QVariant::fromValue( e ) );
9116 }
9117 return true;
9118 }
9119 );
9120 functions << orderPartsFunc;
9121
9122 functions
9123 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9124 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9125 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
9126 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9127 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9128 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
9129 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9130 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
9131 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9132 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9133 fcnLineInterpolatePointByM, QStringLiteral( "GeometryGroup" ) )
9134 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9135 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
9136 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9137 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
9138 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9139 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9140 fcnLineLocateM, QStringLiteral( "GeometryGroup" ) )
9141 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9142 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
9143 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9144 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
9145 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9146 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
9147
9148
9149 // **Record** functions
9150
9151 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
9152 idFunc->setIsStatic( false );
9153 functions << idFunc;
9154
9155 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
9156 currentFeatureFunc->setIsStatic( false );
9157 functions << currentFeatureFunc;
9158
9159 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QStringLiteral( "WithBraces" ) ), fcnUuid, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$uuid" ) );
9160 uuidFunc->setIsStatic( false );
9161 functions << uuidFunc;
9162
9163 functions
9164 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
9165 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9166 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
9167 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
9168 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
9169 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9170 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
9171 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
9172
9173 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
9174 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9175 attributesFunc->setIsStatic( false );
9176 functions << attributesFunc;
9177 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
9178 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9179 representAttributesFunc->setIsStatic( false );
9180 functions << representAttributesFunc;
9181
9182 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
9183 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9184 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9185 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9186 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9187 validateFeature->setIsStatic( false );
9188 functions << validateFeature;
9189
9190 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
9191 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
9192 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9193 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9194 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9195 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9196 validateAttribute->setIsStatic( false );
9197 functions << validateAttribute;
9198
9200 QStringLiteral( "maptip" ),
9201 -1,
9202 fcnFeatureMaptip,
9203 QStringLiteral( "Record and Attributes" ),
9204 QString(),
9205 false,
9206 QSet<QString>()
9207 );
9208 maptipFunc->setIsStatic( false );
9209 functions << maptipFunc;
9210
9212 QStringLiteral( "display_expression" ),
9213 -1,
9214 fcnFeatureDisplayExpression,
9215 QStringLiteral( "Record and Attributes" ),
9216 QString(),
9217 false,
9218 QSet<QString>()
9219 );
9220 displayFunc->setIsStatic( false );
9221 functions << displayFunc;
9222
9224 QStringLiteral( "is_selected" ),
9225 -1,
9226 fcnIsSelected,
9227 QStringLiteral( "Record and Attributes" ),
9228 QString(),
9229 false,
9230 QSet<QString>()
9231 );
9232 isSelectedFunc->setIsStatic( false );
9233 functions << isSelectedFunc;
9234
9235 functions
9237 QStringLiteral( "num_selected" ),
9238 -1,
9239 fcnNumSelected,
9240 QStringLiteral( "Record and Attributes" ),
9241 QString(),
9242 false,
9243 QSet<QString>()
9244 );
9245
9246 functions
9248 QStringLiteral( "sqlite_fetch_and_increment" ),
9250 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
9251 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
9252 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
9253 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
9254 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
9255 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
9256 fcnSqliteFetchAndIncrement,
9257 QStringLiteral( "Record and Attributes" )
9258 );
9259
9260 // **Fields and Values** functions
9261 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
9262
9263 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9264 {
9265 Q_UNUSED( context )
9266 if ( node->args()->count() == 1 )
9267 {
9268 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9269 if ( colRef )
9270 {
9271 return true;
9272 }
9273 else
9274 {
9275 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9276 return false;
9277 }
9278 }
9279 else if ( node->args()->count() == 2 )
9280 {
9281 return true;
9282 }
9283 else
9284 {
9285 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9286 return false;
9287 }
9288 }
9289 );
9290
9291 functions << representValueFunc;
9292
9293 // **General** functions
9294 functions
9295 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9296 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
9297 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
9298 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
9300 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9301 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
9302 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
9303 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
9305 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
9306 fcnMimeType, QStringLiteral( "General" ) )
9307 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9308 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
9309 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
9310
9311 // **var** function
9312 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
9313 varFunction->setIsStaticFunction(
9314 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9315 {
9316 /* A variable node is static if it has a static name and the name can be found at prepare
9317 * time and is tagged with isStatic.
9318 * It is not static if a variable is set during iteration or not tagged isStatic.
9319 * (e.g. geom_part variable)
9320 */
9321 if ( node->args()->count() > 0 )
9322 {
9323 QgsExpressionNode *argNode = node->args()->at( 0 );
9324
9325 if ( !argNode->isStatic( parent, context ) )
9326 return false;
9327
9328 const QString varName = argNode->eval( parent, context ).toString();
9329 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
9330 return false;
9331
9332 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9333 return scope ? scope->isStatic( varName ) : false;
9334 }
9335 return false;
9336 }
9337 );
9338 varFunction->setUsesGeometryFunction(
9339 []( const QgsExpressionNodeFunction * node ) -> bool
9340 {
9341 if ( node && node->args()->count() > 0 )
9342 {
9343 QgsExpressionNode *argNode = node->args()->at( 0 );
9344 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9345 {
9346 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
9347 return true;
9348 }
9349 }
9350 return false;
9351 }
9352 );
9353
9354 functions
9355 << varFunction;
9356
9357 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9358 evalTemplateFunction->setIsStaticFunction(
9359 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9360 {
9361 if ( node->args()->count() > 0 )
9362 {
9363 QgsExpressionNode *argNode = node->args()->at( 0 );
9364
9365 if ( argNode->isStatic( parent, context ) )
9366 {
9367 QString expString = argNode->eval( parent, context ).toString();
9368
9369 QgsExpression e( expString );
9370
9371 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9372 return true;
9373 }
9374 }
9375
9376 return false;
9377 } );
9378 functions << evalTemplateFunction;
9379
9380 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9381 evalFunc->setIsStaticFunction(
9382 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9383 {
9384 if ( node->args()->count() > 0 )
9385 {
9386 QgsExpressionNode *argNode = node->args()->at( 0 );
9387
9388 if ( argNode->isStatic( parent, context ) )
9389 {
9390 QString expString = argNode->eval( parent, context ).toString();
9391
9392 QgsExpression e( expString );
9393
9394 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9395 return true;
9396 }
9397 }
9398
9399 return false;
9400 } );
9401
9402 functions << evalFunc;
9403
9404 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9405 attributeFunc->setIsStaticFunction(
9406 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9407 {
9408 const QList< QgsExpressionNode *> argList = node->args()->list();
9409 for ( QgsExpressionNode *argNode : argList )
9410 {
9411 if ( !argNode->isStatic( parent, context ) )
9412 return false;
9413 }
9414
9415 if ( node->args()->count() == 1 )
9416 {
9417 // not static -- this is the variant which uses the current feature taken direct from the expression context
9418 return false;
9419 }
9420
9421 return true;
9422 } );
9423 functions << attributeFunc;
9424
9425 functions
9426 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9428 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9429 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9430
9431 // functions for arrays
9434 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9435 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9436 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9437 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9438 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9439 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9440 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9441 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9442 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9443 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9444 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9445 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9446 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9447 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9448 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9449 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9450 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9451 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9452 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9453 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9454 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9455 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9456 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9457 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9458 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9459 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9460 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9461 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9462 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9463 << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) )
9464 << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) )
9465 << new QgsStaticExpressionFunction( QStringLiteral( "generate_series" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "stop" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "step" ), true, 1.0 ), fcnGenerateSeries, QStringLiteral( "Arrays" ) )
9466 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9467
9468 //functions for maps
9469 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9470 << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "map_to_json" ) )
9471 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9472 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9473 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9474 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9475 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9476 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9477 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9478 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9479 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9480 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9481 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9482 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9483 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9484 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9485 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9486 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9487 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9488 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9489 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9490
9491 ;
9492
9494
9495 //QgsExpression has ownership of all built-in functions
9496 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9497 {
9498 *sOwnedFunctions() << func;
9499 *sBuiltinFunctions() << func->name();
9500 sBuiltinFunctions()->append( func->aliases() );
9501 }
9502 }
9503 return functions;
9504}
9505
9506bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9507{
9508 int fnIdx = functionIndex( function->name() );
9509 if ( fnIdx != -1 )
9510 {
9511 return false;
9512 }
9513
9514 QMutexLocker locker( &sFunctionsMutex );
9515 sFunctions()->append( function );
9516 if ( transferOwnership )
9517 sOwnedFunctions()->append( function );
9518
9519 return true;
9520}
9521
9522bool QgsExpression::unregisterFunction( const QString &name )
9523{
9524 // You can never override the built in functions.
9525 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9526 {
9527 return false;
9528 }
9529 int fnIdx = functionIndex( name );
9530 if ( fnIdx != -1 )
9531 {
9532 QMutexLocker locker( &sFunctionsMutex );
9533 sFunctions()->removeAt( fnIdx );
9534 sFunctionIndexMap.clear();
9535 return true;
9536 }
9537 return false;
9538}
9539
9541{
9542 qDeleteAll( *sOwnedFunctions() );
9543 sOwnedFunctions()->clear();
9545
9546const QStringList &QgsExpression::BuiltinFunctions()
9547{
9548 if ( sBuiltinFunctions()->isEmpty() )
9549 {
9550 Functions(); // this method builds the gmBuiltinFunctions as well
9551 }
9552 return *sBuiltinFunctions();
9554
9556 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9557 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9558 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9559 QStringLiteral( "Arrays" ) )
9560{
9561
9562}
9563
9565{
9566 bool isStatic = false;
9567
9568 QgsExpressionNode::NodeList *args = node->args();
9570 if ( args->count() < 2 )
9571 return false;
9572
9573 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9574 {
9575 isStatic = true;
9576 }
9577 return isStatic;
9578}
9579
9581{
9582 Q_UNUSED( node )
9583 QVariantList result;
9584
9585 if ( args->count() < 2 )
9586 // error
9587 return result;
9588
9589 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9590
9591 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9592 std::unique_ptr< QgsExpressionContext > tempContext;
9593 if ( !subContext )
9594 {
9595 tempContext = std::make_unique< QgsExpressionContext >();
9596 subContext = tempContext.get();
9597 }
9598
9600 subContext->appendScope( subScope );
9601
9602 int i = 0;
9603 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
9604 {
9605 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9606 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), i, true ) );
9607 result << args->at( 1 )->eval( parent, subContext );
9608 }
9609
9610 if ( context )
9611 delete subContext->popScope();
9612
9613 return result;
9614}
9615
9616QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9618 // This is a dummy function, all the real handling is in run
9619 Q_UNUSED( values )
9620 Q_UNUSED( context )
9621 Q_UNUSED( parent )
9622 Q_UNUSED( node )
9623
9624 Q_ASSERT( false );
9625 return QVariant();
9626}
9627
9629{
9630 QgsExpressionNode::NodeList *args = node->args();
9631
9632 if ( args->count() < 2 )
9633 // error
9634 return false;
9635
9636 args->at( 0 )->prepare( parent, context );
9637
9638 QgsExpressionContext subContext;
9639 if ( context )
9640 subContext = *context;
9643 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9644 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), QVariant(), true ) );
9645 subContext.appendScope( subScope );
9646
9647 args->at( 1 )->prepare( parent, &subContext );
9648
9649 return true;
9650}
9653 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9654 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9655 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9656 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9657 QStringLiteral( "Arrays" ) )
9658{
9659
9660}
9661
9663{
9664 bool isStatic = false;
9665
9666 QgsExpressionNode::NodeList *args = node->args();
9668 if ( args->count() < 2 )
9669 return false;
9670
9671 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9672 {
9673 isStatic = true;
9674 }
9675 return isStatic;
9676}
9677
9679{
9680 Q_UNUSED( node )
9681 QVariantList result;
9682
9683 if ( args->count() < 2 )
9684 // error
9685 return result;
9686
9687 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9688
9689 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9690 std::unique_ptr< QgsExpressionContext > tempContext;
9691 if ( !subContext )
9692 {
9693 tempContext = std::make_unique< QgsExpressionContext >();
9694 subContext = tempContext.get();
9695 }
9696
9698 subContext->appendScope( subScope );
9699
9700 int limit = 0;
9701 if ( args->count() >= 3 )
9702 {
9703 const QVariant limitVar = args->at( 2 )->eval( parent, context );
9704
9705 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
9706 {
9707 limit = limitVar.toInt();
9708 }
9709 else
9710 {
9711 return result;
9712 }
9713 }
9714
9715 for ( const QVariant &value : array )
9716 {
9717 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
9718 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
9719 {
9720 result << value;
9721
9722 if ( limit > 0 && limit == result.size() )
9723 break;
9724 }
9725 }
9726
9727 if ( context )
9728 delete subContext->popScope();
9729
9730 return result;
9731}
9732
9733QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9735 // This is a dummy function, all the real handling is in run
9736 Q_UNUSED( values )
9737 Q_UNUSED( context )
9738 Q_UNUSED( parent )
9739 Q_UNUSED( node )
9740
9741 Q_ASSERT( false );
9742 return QVariant();
9743}
9744
9746{
9747 QgsExpressionNode::NodeList *args = node->args();
9748
9749 if ( args->count() < 2 )
9750 // error
9751 return false;
9752
9753 args->at( 0 )->prepare( parent, context );
9754
9755 QgsExpressionContext subContext;
9756 if ( context )
9757 subContext = *context;
9758
9760 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9761 subContext.appendScope( subScope );
9762
9763 args->at( 1 )->prepare( parent, &subContext );
9764
9765 return true;
9768 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
9769 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
9770 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
9771 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9772 QStringLiteral( "General" ) )
9773{
9774
9775}
9776
9778{
9779 bool isStatic = false;
9780
9781 QgsExpressionNode::NodeList *args = node->args();
9782
9783 if ( args->count() < 3 )
9784 return false;
9785
9786 // We only need to check if the node evaluation is static, if both - name and value - are static.
9787 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9788 {
9789 QVariant name = args->at( 0 )->eval( parent, context );
9790 QVariant value = args->at( 1 )->eval( parent, context );
9792 // Temporarily append a new scope to provide the variable
9793 appendTemporaryVariable( context, name.toString(), value );
9794 if ( args->at( 2 )->isStatic( parent, context ) )
9795 isStatic = true;
9796 popTemporaryVariable( context );
9797 }
9798
9799 return isStatic;
9800}
9801
9803{
9804 Q_UNUSED( node )
9805 QVariant result;
9806
9807 if ( args->count() < 3 )
9808 // error
9809 return result;
9810
9811 QVariant name = args->at( 0 )->eval( parent, context );
9812 QVariant value = args->at( 1 )->eval( parent, context );
9813
9814 const QgsExpressionContext *updatedContext = context;
9815 std::unique_ptr< QgsExpressionContext > tempContext;
9816 if ( !updatedContext )
9817 {
9818 tempContext = std::make_unique< QgsExpressionContext >();
9819 updatedContext = tempContext.get();
9821
9822 appendTemporaryVariable( updatedContext, name.toString(), value );
9823 result = args->at( 2 )->eval( parent, updatedContext );
9824
9825 if ( context )
9826 popTemporaryVariable( updatedContext );
9827
9828 return result;
9829}
9830
9831QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9833 // This is a dummy function, all the real handling is in run
9834 Q_UNUSED( values )
9835 Q_UNUSED( context )
9836 Q_UNUSED( parent )
9837 Q_UNUSED( node )
9838
9839 Q_ASSERT( false );
9840 return QVariant();
9841}
9842
9844{
9845 QgsExpressionNode::NodeList *args = node->args();
9846
9847 if ( args->count() < 3 )
9848 // error
9849 return false;
9850
9851 QVariant name = args->at( 0 )->prepare( parent, context );
9852 QVariant value = args->at( 1 )->prepare( parent, context );
9853
9854 const QgsExpressionContext *updatedContext = context;
9855 std::unique_ptr< QgsExpressionContext > tempContext;
9856 if ( !updatedContext )
9857 {
9858 tempContext = std::make_unique< QgsExpressionContext >();
9859 updatedContext = tempContext.get();
9860 }
9861
9862 appendTemporaryVariable( updatedContext, name.toString(), value );
9863 args->at( 2 )->prepare( parent, updatedContext );
9864
9865 if ( context )
9866 popTemporaryVariable( updatedContext );
9867
9868 return true;
9869}
9870
9871void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
9872{
9873 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
9874 delete updatedContext->popScope();
9875}
9876
9877void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
9878{
9881
9882 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
9883 updatedContext->appendScope( scope );
9884}
@ Left
Buffer to left of line.
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3058
@ ScaleDashOnly
Only dash lengths are adjusted.
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
@ ScaleGapOnly
Only gap lengths are adjusted.
@ Success
Operation succeeded.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
JoinStyle
Join styles for buffers.
Definition qgis.h:1968
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:5466
@ StdDev
Standard deviation.
@ NoStatistic
No statistic.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:1955
@ Flat
Flat cap (in line with start/end of line)
@ Round
Round cap.
@ Square
Square cap (extends past start/end of line by buffer distance)
Aggregate
Available aggregates to calculate.
Definition qgis.h:5343
@ StringMinimumLength
Minimum length of string (string fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Mean
Mean of values (numeric fields only)
@ Median
Median of values (numeric fields only)
@ Max
Max of values.
@ Min
Min of values.
@ StringMaximumLength
Maximum length of string (string fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ Sum
Sum of values.
@ Minority
Minority of values.
@ CountMissing
Number of missing (null) values.
@ ArrayAggregate
Create an array of values.
@ Majority
Majority of values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ CountDistinct
Number of distinct values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3043
@ HalfDash
Start or finish the pattern with a half length dash.
@ HalfGap
Start or finish the pattern with a half length gap.
@ FullGap
Start or finish the pattern with a full gap.
@ FullDash
Start or finish the pattern with a full dash.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2014
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
@ PointM
PointM.
@ PointZ
PointZ.
@ PointZM
PointZM.
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Handles the array_filter(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
Handles the array loopingarray_Foreach(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:43
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:35
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:272
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Ellipse geometry type.
Definition qgsellipse.h:39
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
A abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:739
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 makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
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.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
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, exception handling*.
Definition qgsgeos.h:137
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2724
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
A representation of the interval between two datetime values.
Definition qgsinterval.h:46
bool isValid() const
Returns true if the interval is valid.
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
double years() const
Returns the interval duration in years (based on an average year length)
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:76
QString name
Definition qgsmaplayer.h:80
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:79
QgsLayerMetadata metadata
Definition qgsmaplayer.h:82
Qgis::LayerType type
Definition qgsmaplayer.h:86
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString mapTipTemplate
Definition qgsmaplayer.h:89
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:693
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:557
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:425
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:104
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:705
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
QgsPointXY center() const
Returns the center point of the rectangle.
void grow(double delta)
Grows the rectangle in place by the specified amount.
double height() const
Returns the height of the rectangle.
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices)
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:48
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
c++ helper class for defining QgsExpression functions.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
Utility functions for working with strings.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition qgsstyle.cpp:500
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:145
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
Triangle geometry type.
Definition qgstriangle.h:33
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
Handles the with_variable(name, value, node) expression function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
uint qHash(const QVariant &variant)
Hash for QVariant.
Definition qgis.cpp:198
#define str(x)
Definition qgis.cpp:38
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:120
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6434
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6433
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:5898
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5857
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QList< QgsExpressionFunction * > ExpressionFunctionList
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColor(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< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:80
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
int precision
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:62
const QgsMapLayer * layer
Definition qgsogcutils.h:72
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:73
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30