QGIS API Documentation 3.43.0-Master (a93bf8b6462)
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 argValues.reserve( argList.size() );
100 for ( QgsExpressionNode *n : argList )
101 {
102 QVariant v;
103 if ( lazyEval() )
104 {
105 // Pass in the node for the function to eval as it needs.
106 v = QVariant::fromValue( n );
107 }
108 else
109 {
110 v = n->eval( parent, context );
112 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
113 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
114 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
115 }
116 argValues.append( v );
117 arg++;
118 }
119 }
120
121 return func( argValues, context, parent, node );
122}
123
125{
126 Q_UNUSED( node )
127 return true;
128}
129
131{
132 return QStringList();
133}
134
136{
137 Q_UNUSED( parent )
138 Q_UNUSED( context )
139 Q_UNUSED( node )
140 return false;
141}
142
144{
145 Q_UNUSED( parent )
146 Q_UNUSED( context )
147 Q_UNUSED( node )
148 return true;
149}
150
152{
153 Q_UNUSED( node )
154 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
155}
156
158{
159 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
160}
161
163{
164 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
165}
166
168{
169 return mHandlesNull;
170}
171
172// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
175 FcnEval fcn,
176 const QString &group,
177 const QString &helpText,
178 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
179 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
180 bool lazyEval,
181 const QStringList &aliases,
182 bool handlesNull )
183 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
184 , mFnc( fcn )
185 , mAliases( aliases )
186 , mUsesGeometry( false )
187 , mUsesGeometryFunc( usesGeometry )
188 , mReferencedColumnsFunc( referencedColumns )
189{
190}
192
194{
195 return mAliases;
196}
197
199{
200 if ( mUsesGeometryFunc )
201 return mUsesGeometryFunc( node );
202 else
203 return mUsesGeometry;
204}
205
206void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
207{
208 mUsesGeometryFunc = usesGeometry;
209}
210
212{
213 if ( mReferencedColumnsFunc )
214 return mReferencedColumnsFunc( node );
215 else
216 return mReferencedColumns;
217}
218
220{
221 if ( mIsStaticFunc )
222 return mIsStaticFunc( node, parent, context );
223 else
224 return mIsStatic;
225}
226
228{
229 if ( mPrepareFunc )
230 return mPrepareFunc( node, parent, context );
231
232 return true;
233}
234
236{
237 mIsStaticFunc = isStatic;
238}
239
241{
242 mIsStaticFunc = nullptr;
243 mIsStatic = isStatic;
244}
245
246void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
247{
248 mPrepareFunc = prepareFunc;
249}
250
252{
253 if ( node && node->args() )
254 {
255 const QList< QgsExpressionNode * > argList = node->args()->list();
256 for ( QgsExpressionNode *argNode : argList )
257 {
258 if ( !argNode->isStatic( parent, context ) )
259 return false;
260 }
261 }
262
263 return true;
264}
265
266static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
267{
268 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
269 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
270 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
271
272 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
273 return QVariant();
274
275 QVariantList array;
276 int length = 1;
277
278 array << start;
279 double current = start + step;
280 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
281 {
282 array << current;
283 current += step;
284 length++;
285 }
286
287 return array;
288}
289
290static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
291{
292 if ( !context )
293 return QVariant();
294
295 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
296
297 if ( name == QLatin1String( "feature" ) )
298 {
299 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
300 }
301 else if ( name == QLatin1String( "id" ) )
302 {
303 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
304 }
305 else if ( name == QLatin1String( "geometry" ) )
306 {
307 if ( !context->hasFeature() )
308 return QVariant();
309
310 const QgsFeature feature = context->feature();
311 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
312 }
313 else
314 {
315 return context->variable( name );
316 }
317}
318
319static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
320{
321 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
322 return QgsExpression::replaceExpressionText( templateString, context );
323}
324
325static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
326{
327 if ( !context )
328 return QVariant();
329
330 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
331 QgsExpression expression( expString );
332 return expression.evaluate( context );
333}
334
335static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
336{
337 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
338 return QVariant( std::sqrt( x ) );
339}
340
341static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
342{
343 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
344 return QVariant( std::fabs( val ) );
345}
346
347static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
348{
349 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
350 return ( deg * M_PI ) / 180;
351}
352static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
353{
354 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
355 return ( 180 * rad ) / M_PI;
356}
357static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
358{
359 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
360 return QVariant( std::sin( x ) );
361}
362static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
363{
364 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
365 return QVariant( std::cos( x ) );
366}
367static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
368{
369 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
370 return QVariant( std::tan( x ) );
371}
372static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
373{
374 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
375 return QVariant( std::asin( x ) );
376}
377static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
378{
379 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
380 return QVariant( std::acos( x ) );
381}
382static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
383{
384 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
385 return QVariant( std::atan( x ) );
386}
387static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
388{
389 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
390 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
391 return QVariant( std::atan2( y, x ) );
392}
393static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
394{
395 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
396 return QVariant( std::exp( x ) );
397}
398static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
399{
400 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
401 if ( x <= 0 )
402 return QVariant();
403 return QVariant( std::log( x ) );
404}
405static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
406{
407 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
408 if ( x <= 0 )
409 return QVariant();
410 return QVariant( log10( x ) );
411}
412static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
413{
414 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
415 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
416 if ( x <= 0 || b <= 0 )
417 return QVariant();
418 return QVariant( std::log( x ) / std::log( b ) );
419}
420static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
421{
422 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
423 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
424 if ( max < min )
425 return QVariant();
426
427 std::random_device rd;
428 std::mt19937_64 generator( rd() );
429
430 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
431 {
432 quint32 seed;
433 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
434 {
435 // if seed can be converted to int, we use as is
436 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
437 }
438 else
439 {
440 // if not, we hash string representation to int
441 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
442 std::hash<std::string> hasher;
443 seed = hasher( seedStr.toStdString() );
444 }
445 generator.seed( seed );
446 }
447
448 // Return a random double in the range [min, max] (inclusive)
449 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
450 return QVariant( min + f * ( max - min ) );
451}
452static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
453{
454 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
455 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
456 if ( max < min )
457 return QVariant();
458
459 std::random_device rd;
460 std::mt19937_64 generator( rd() );
461
462 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
463 {
464 quint32 seed;
465 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
466 {
467 // if seed can be converted to int, we use as is
468 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
469 }
470 else
471 {
472 // if not, we hash string representation to int
473 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
474 std::hash<std::string> hasher;
475 seed = hasher( seedStr.toStdString() );
476 }
477 generator.seed( seed );
478 }
479
480 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
481 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
482 return QVariant( randomInteger );
483
484 // Prevent wrong conversion of QVariant. See #36412
485 return QVariant( int( randomInteger ) );
486}
487
488static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
489{
490 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
491 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
492 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
493 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
494 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
495
496 if ( domainMin >= domainMax )
497 {
498 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
499 return QVariant();
500 }
501
502 // outside of domain?
503 if ( val >= domainMax )
504 {
505 return rangeMax;
506 }
507 else if ( val <= domainMin )
508 {
509 return rangeMin;
510 }
511
512 // calculate linear scale
513 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
514 double c = rangeMin - ( domainMin * m );
515
516 // Return linearly scaled value
517 return QVariant( m * val + c );
518}
519
520static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
521{
522 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
523 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
524 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
525 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
526 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
527 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
528
529 if ( domainMin >= domainMax )
530 {
531 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
532 return QVariant();
533 }
534 if ( exponent <= 0 )
535 {
536 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
537 return QVariant();
538 }
539
540 // outside of domain?
541 if ( val >= domainMax )
542 {
543 return rangeMax;
544 }
545 else if ( val <= domainMin )
546 {
547 return rangeMin;
548 }
549
550 // Return polynomially scaled value
551 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
552}
553
554static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
555{
556 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
557 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
558 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
559 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
560 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
561 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
562
563 if ( domainMin >= domainMax )
564 {
565 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
566 return QVariant();
567 }
568 if ( exponent <= 0 )
569 {
570 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
571 return QVariant();
572 }
573
574 // outside of domain?
575 if ( val >= domainMax )
576 {
577 return rangeMax;
578 }
579 else if ( val <= domainMin )
580 {
581 return rangeMin;
582 }
583
584 // Return exponentially scaled value
585 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
586 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
587}
588
589static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
590{
591 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
592 double maxVal = std::numeric_limits<double>::quiet_NaN();
593 for ( const QVariant &val : values )
594 {
595 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
596 if ( std::isnan( maxVal ) )
597 {
598 maxVal = testVal;
599 }
600 else if ( !std::isnan( testVal ) )
601 {
602 maxVal = std::max( maxVal, testVal );
603 }
604 }
605
606 if ( !std::isnan( maxVal ) )
607 {
608 result = QVariant( maxVal );
609 }
610 return result;
611}
612
613static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
614{
615 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
616 double minVal = std::numeric_limits<double>::quiet_NaN();
617 for ( const QVariant &val : values )
618 {
619 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
620 if ( std::isnan( minVal ) )
621 {
622 minVal = testVal;
623 }
624 else if ( !std::isnan( testVal ) )
625 {
626 minVal = std::min( minVal, testVal );
627 }
628 }
629
630 if ( !std::isnan( minVal ) )
631 {
632 result = QVariant( minVal );
633 }
634 return result;
635}
636
637static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
638{
639 //lazy eval, so we need to evaluate nodes now
640
641 //first node is layer id or name
642 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
644 QVariant value = node->eval( parent, context );
646
647 // TODO this expression function is NOT thread safe
649 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
651 if ( !vl )
652 {
653 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
654 return QVariant();
655 }
656
657 // second node is aggregate type
658 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
660 value = node->eval( parent, context );
662 bool ok = false;
663 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
664 if ( !ok )
665 {
666 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
667 return QVariant();
668 }
669
670 // third node is subexpression (or field name)
671 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
673 QString subExpression = node->dump();
674
676 //optional forth node is filter
677 if ( values.count() > 3 )
678 {
679 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
681 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
682 if ( !nl || nl->value().isValid() )
683 parameters.filter = node->dump();
684 }
685
686 //optional fifth node is concatenator
687 if ( values.count() > 4 )
688 {
689 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
691 value = node->eval( parent, context );
693 parameters.delimiter = value.toString();
694 }
695
696 //optional sixth node is order by
697 QString orderBy;
698 if ( values.count() > 5 )
699 {
700 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
702 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
703 if ( !nl || nl->value().isValid() )
704 {
705 orderBy = node->dump();
706 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
707 }
708 }
709
710 QString aggregateError;
711 QVariant result;
712 if ( context )
713 {
714 QString cacheKey;
715 QgsExpression subExp( subExpression );
716 QgsExpression filterExp( parameters.filter );
717
718 const QSet< QString > filterVars = filterExp.referencedVariables();
719 const QSet< QString > subExpVars = subExp.referencedVariables();
720 QSet<QString> allVars = filterVars + subExpVars;
721
722 bool isStatic = true;
723 if ( filterVars.contains( QStringLiteral( "parent" ) )
724 || filterVars.contains( QString() )
725 || subExpVars.contains( QStringLiteral( "parent" ) )
726 || subExpVars.contains( QString() ) )
727 {
728 isStatic = false;
729 }
730 else
731 {
732 for ( const QString &varName : allVars )
733 {
734 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
735 if ( scope && !scope->isStatic( varName ) )
736 {
737 isStatic = false;
738 break;
739 }
740 }
741 }
742
743 if ( isStatic && ! parameters.orderBy.isEmpty() )
744 {
745 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
746 {
747 const QgsExpression &orderByExpression { orderByClause.expression() };
748 if ( orderByExpression.referencedVariables().contains( QStringLiteral( "parent" ) ) || orderByExpression.referencedVariables().contains( QString() ) )
749 {
750 isStatic = false;
751 break;
752 }
753 }
754 }
755
756 if ( !isStatic )
757 {
758 bool ok = false;
759 const QString contextHash = context->uniqueHash( ok, allVars );
760 if ( ok )
761 {
762 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
763 orderBy, contextHash );
764 }
765 }
766 else
767 {
768 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
769 }
770
771 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
772 {
773 return context->cachedValue( cacheKey );
774 }
775
776 QgsExpressionContext subContext( *context );
778 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
779 subContext.appendScope( subScope );
780 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
781
782 if ( ok && !cacheKey.isEmpty() )
783 {
784 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
785 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
786 // associated with it's calculation!
787 context->setCachedValue( cacheKey, result );
788 }
789 }
790 else
791 {
792 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
793 }
794 if ( !ok )
795 {
796 if ( !aggregateError.isEmpty() )
797 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
798 else
799 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
800 return QVariant();
801 }
802
803 return result;
804}
805
806static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
807{
808 if ( !context )
809 {
810 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
811 return QVariant();
812 }
813
814 // first step - find current layer
815
816 // TODO this expression function is NOT thread safe
818 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
820 if ( !vl )
821 {
822 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
823 return QVariant();
824 }
825
826 //lazy eval, so we need to evaluate nodes now
827
828 //first node is relation name
829 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
831 QVariant value = node->eval( parent, context );
833 QString relationId = value.toString();
834 // check relation exists
835 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
836 if ( !relation.isValid() || relation.referencedLayer() != vl )
837 {
838 // check for relations by name
839 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
840 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
841 {
842 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
843 return QVariant();
844 }
845 else
846 {
847 relation = relations.at( 0 );
848 }
849 }
850
851 QgsVectorLayer *childLayer = relation.referencingLayer();
852
853 // second node is aggregate type
854 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
856 value = node->eval( parent, context );
858 bool ok = false;
859 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
860 if ( !ok )
861 {
862 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
863 return QVariant();
864 }
865
866 //third node is subexpression (or field name)
867 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
869 QString subExpression = node->dump();
870
871 //optional fourth node is concatenator
873 if ( values.count() > 3 )
874 {
875 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
877 value = node->eval( parent, context );
879 parameters.delimiter = value.toString();
880 }
881
882 //optional fifth node is order by
883 QString orderBy;
884 if ( values.count() > 4 )
885 {
886 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
888 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
889 if ( !nl || nl->value().isValid() )
890 {
891 orderBy = node->dump();
892 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
893 }
894 }
895
896 if ( !context->hasFeature() )
897 return QVariant();
898 QgsFeature f = context->feature();
899
900 parameters.filter = relation.getRelatedFeaturesFilter( f );
901
902 const QString cacheKey = QStringLiteral( "relagg:%1%:%2:%3:%4:%5:%6" ).arg( relationId, vl->id(),
903 QString::number( static_cast< int >( aggregate ) ),
904 subExpression,
905 parameters.filter,
906 orderBy );
907 if ( context->hasCachedValue( cacheKey ) )
908 return context->cachedValue( cacheKey );
909
910 QVariant result;
911 ok = false;
912
913
914 QgsExpressionContext subContext( *context );
915 QString error;
916 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
917
918 if ( !ok )
919 {
920 if ( !error.isEmpty() )
921 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
922 else
923 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
924 return QVariant();
925 }
926
927 // cache value
928 context->setCachedValue( cacheKey, result );
929 return result;
930}
931
932
933static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
934{
935 if ( !context )
936 {
937 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
938 return QVariant();
939 }
940
941 // first step - find current layer
942
943 // TODO this expression function is NOT thread safe
945 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
947 if ( !vl )
948 {
949 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
950 return QVariant();
951 }
952
953 //lazy eval, so we need to evaluate nodes now
954
955 //first node is subexpression (or field name)
956 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
958 QString subExpression = node->dump();
959
960 //optional second node is group by
961 QString groupBy;
962 if ( values.count() > 1 )
963 {
964 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
966 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
967 if ( !nl || nl->value().isValid() )
968 groupBy = node->dump();
969 }
970
971 //optional third node is filter
972 if ( values.count() > 2 )
973 {
974 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
976 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
977 if ( !nl || nl->value().isValid() )
978 parameters.filter = node->dump();
979 }
980
981 //optional order by node, if supported
982 QString orderBy;
983 if ( orderByPos >= 0 && values.count() > orderByPos )
984 {
985 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
987 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
988 if ( !nl || nl->value().isValid() )
989 {
990 orderBy = node->dump();
991 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
992 }
993 }
994
995 // build up filter with group by
996
997 // find current group by value
998 if ( !groupBy.isEmpty() )
999 {
1000 QgsExpression groupByExp( groupBy );
1001 QVariant groupByValue = groupByExp.evaluate( context );
1002 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
1003 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
1004 QgsExpression::quotedValue( groupByValue ) );
1005 if ( !parameters.filter.isEmpty() )
1006 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
1007 else
1008 parameters.filter = groupByClause;
1009 }
1010
1011 QgsExpression subExp( subExpression );
1012 QgsExpression filterExp( parameters.filter );
1013
1014 bool isStatic = true;
1015 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1016 for ( const QString &varName : refVars )
1017 {
1018 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1019 if ( scope && !scope->isStatic( varName ) )
1020 {
1021 isStatic = false;
1022 break;
1023 }
1024 }
1025
1026 QString cacheKey;
1027 if ( !isStatic )
1028 {
1029 bool ok = false;
1030 const QString contextHash = context->uniqueHash( ok, refVars );
1031 if ( ok )
1032 {
1033 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1034 orderBy, contextHash );
1035 }
1036 }
1037 else
1038 {
1039 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1040 }
1041
1042 if ( context->hasCachedValue( cacheKey ) )
1043 return context->cachedValue( cacheKey );
1044
1045 QVariant result;
1046 bool ok = false;
1047
1048 QgsExpressionContext subContext( *context );
1050 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
1051 subContext.appendScope( subScope );
1052 QString error;
1053 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1054
1055 if ( !ok )
1056 {
1057 if ( !error.isEmpty() )
1058 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1059 else
1060 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1061 return QVariant();
1062 }
1063
1064 // cache value
1065 context->setCachedValue( cacheKey, result );
1066 return result;
1067}
1068
1069
1070static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1071{
1072 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1073}
1074
1075static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1076{
1077 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1078}
1079
1080static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1081{
1082 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1083}
1084
1085static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1086{
1087 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1088}
1089
1090static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1091{
1092 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1093}
1094
1095static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1096{
1097 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1098}
1099
1100static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1101{
1102 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1103}
1104
1105static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1106{
1107 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1108}
1109
1110static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1111{
1112 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1113}
1114
1115static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1116{
1117 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1118}
1119
1120static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1121{
1122 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1123}
1124
1125static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1126{
1127 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1128}
1129
1130static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1131{
1132 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1133}
1134
1135static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1136{
1137 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1138}
1139
1140static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1141{
1142 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1143}
1144
1145static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1146{
1147 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1148}
1149
1150static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1151{
1152 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1153}
1154
1155static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1156{
1157 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1158}
1159
1160static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1161{
1163
1164 //fourth node is concatenator
1165 if ( values.count() > 3 )
1166 {
1167 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1169 QVariant value = node->eval( parent, context );
1171 parameters.delimiter = value.toString();
1172 }
1173
1174 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1175}
1176
1177static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1178{
1180
1181 //fourth node is concatenator
1182 if ( values.count() > 3 )
1183 {
1184 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1186 QVariant value = node->eval( parent, context );
1188 parameters.delimiter = value.toString();
1189 }
1190
1191 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1192}
1193
1194static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1195{
1196 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1197}
1198
1199static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1200{
1201 if ( !context )
1202 return QVariant();
1203
1204 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1205 bool ok = false;
1206 if ( QgsVariantUtils::isNull( scale ) )
1207 return QVariant();
1208
1209 const double v = scale.toDouble( &ok );
1210 if ( ok )
1211 return v;
1212 return QVariant();
1213}
1214
1215static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1216{
1217 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1218 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1219 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1220
1221 // force testValue to sit inside the range specified by the min and max value
1222 if ( testValue <= minValue )
1223 {
1224 return QVariant( minValue );
1225 }
1226 else if ( testValue >= maxValue )
1227 {
1228 return QVariant( maxValue );
1229 }
1230 else
1231 {
1232 return QVariant( testValue );
1233 }
1234}
1235
1236static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1237{
1238 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1239 return QVariant( std::floor( x ) );
1240}
1241
1242static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1243{
1244 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1245 return QVariant( std::ceil( x ) );
1246}
1247
1248static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1249{
1250 const QVariant value = values.at( 0 );
1251 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1252 {
1253 return QVariant( false );
1254 }
1255 else if ( value.userType() == QMetaType::QString )
1256 {
1257 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1258 return QVariant( !value.toString().isEmpty() );
1259 }
1260 else if ( QgsExpressionUtils::isList( value ) )
1261 {
1262 return !value.toList().isEmpty();
1263 }
1264 return QVariant( value.toBool() );
1265}
1266static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1267{
1268 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1269}
1270static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1271{
1272 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1273}
1274static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1275{
1276 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1277}
1278
1279static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1280{
1281 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1282 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1283 if ( format.isEmpty() && !language.isEmpty() )
1284 {
1285 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1286 return QVariant( QDateTime() );
1287 }
1288
1289 if ( format.isEmpty() && language.isEmpty() )
1290 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1291
1292 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1293 QLocale locale = QLocale();
1294 if ( !language.isEmpty() )
1295 {
1296 locale = QLocale( language );
1297 }
1298
1299 QDateTime datetime = locale.toDateTime( datetimestring, format );
1300 if ( !datetime.isValid() )
1301 {
1302 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1303 datetime = QDateTime();
1304 }
1305 return QVariant( datetime );
1306}
1307
1308static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1309{
1310 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1311 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1312 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1313
1314 const QDate date( year, month, day );
1315 if ( !date.isValid() )
1316 {
1317 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1318 return QVariant();
1319 }
1320 return QVariant( date );
1321}
1322
1323static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1324{
1325 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1326 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1327 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1328
1329 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1330 if ( !time.isValid() )
1331 {
1332 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1333 return QVariant();
1334 }
1335 return QVariant( time );
1336}
1337
1338static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1339{
1340 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1341 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1342 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1343 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1344 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1345 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1346
1347 const QDate date( year, month, day );
1348 if ( !date.isValid() )
1349 {
1350 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1351 return QVariant();
1352 }
1353 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1354 if ( !time.isValid() )
1355 {
1356 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1357 return QVariant();
1358 }
1359 return QVariant( QDateTime( date, time ) );
1360}
1361
1362static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1363{
1364 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1365 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1366 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1367 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1368 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1369 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1370 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1371
1372 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1373}
1374
1375static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1376{
1377 for ( const QVariant &value : values )
1378 {
1379 if ( QgsVariantUtils::isNull( value ) )
1380 continue;
1381 return value;
1382 }
1383 return QVariant();
1384}
1385
1386static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1387{
1388 const QVariant val1 = values.at( 0 );
1389 const QVariant val2 = values.at( 1 );
1390
1391 if ( val1 == val2 )
1392 return QVariant();
1393 else
1394 return val1;
1395}
1396
1397static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1398{
1399 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1400 return QVariant( str.toLower() );
1401}
1402static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1403{
1404 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1405 return QVariant( str.toUpper() );
1406}
1407static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1408{
1409 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1410 QStringList elems = str.split( ' ' );
1411 for ( int i = 0; i < elems.size(); i++ )
1412 {
1413 if ( elems[i].size() > 1 )
1414 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1415 }
1416 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1417}
1418
1419static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1420{
1421 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1422 return QVariant( str.trimmed() );
1423}
1424
1425static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1426{
1427 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1428
1429 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1430
1431 const QRegularExpression re( QStringLiteral( "^([%1]*)" ).arg( QRegularExpression::escape( characters ) ) );
1432 str.replace( re, QString() );
1433 return QVariant( str );
1434}
1435
1436static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1437{
1438 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1439
1440 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1441
1442 const QRegularExpression re( QStringLiteral( "([%1]*)$" ).arg( QRegularExpression::escape( characters ) ) );
1443 str.replace( re, QString() );
1444 return QVariant( str );
1445}
1446
1447static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1448{
1449 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1450 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1451 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1452}
1453
1454static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1455{
1456 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1457 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1458 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1459}
1460
1461static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1462{
1463 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1464 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1465 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1466 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1467}
1468
1469static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1470{
1471 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1472 return QVariant( QgsStringUtils::soundex( string ) );
1473}
1474
1475static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1476{
1477 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1478 return QVariant( QString( character ) );
1479}
1480
1481static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1482{
1483 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1484
1485 if ( value.isEmpty() )
1486 {
1487 return QVariant();
1488 }
1489
1490 int res = value.at( 0 ).unicode();
1491 return QVariant( res );
1492}
1493
1494static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1495{
1496 if ( values.length() == 2 || values.length() == 3 )
1497 {
1498 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1499 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1500
1501 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1502
1503 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1504 }
1505
1506 return QVariant();
1507}
1508
1509static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1510{
1511 // two variants, one for geometry, one for string
1512
1513 //geometry variant
1514 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1515 if ( !geom.isNull() )
1516 {
1517 if ( geom.type() == Qgis::GeometryType::Line )
1518 return QVariant( geom.length() );
1519 else
1520 return QVariant();
1521 }
1522
1523 //otherwise fall back to string variant
1524 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1525 return QVariant( str.length() );
1526}
1527
1528static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1529{
1530 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1531
1532 if ( geom.type() != Qgis::GeometryType::Line )
1533 return QVariant();
1534
1535 double totalLength = 0;
1536 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1537 {
1538 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1539 {
1540 totalLength += line->length3D();
1541 }
1542 else
1543 {
1544 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1545 totalLength += segmentized->length3D();
1546 }
1547 }
1548
1549 return totalLength;
1550}
1551
1552
1553static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1554{
1555 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1556 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1557 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1558}
1559
1560static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1561{
1562 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1563 {
1564 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1565 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1566 QVector< QPair< QString, QString > > mapItems;
1567
1568 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1569 {
1570 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1571 }
1572
1573 // larger keys should be replaced first since they may contain whole smaller keys
1574 std::sort( mapItems.begin(),
1575 mapItems.end(),
1576 []( const QPair< QString, QString > &pair1,
1577 const QPair< QString, QString > &pair2 )
1578 {
1579 return ( pair1.first.length() > pair2.first.length() );
1580 } );
1581
1582 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1583 {
1584 str = str.replace( it->first, it->second );
1585 }
1586
1587 return QVariant( str );
1588 }
1589 else if ( values.count() == 3 )
1590 {
1591 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1592 QVariantList before;
1593 QVariantList after;
1594 bool isSingleReplacement = false;
1595
1596 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1597 {
1598 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1599 }
1600 else
1601 {
1602 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1603 }
1604
1605 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1606 {
1607 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1608 isSingleReplacement = true;
1609 }
1610 else
1611 {
1612 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1613 }
1614
1615 if ( !isSingleReplacement && before.length() != after.length() )
1616 {
1617 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1618 return QVariant();
1619 }
1620
1621 for ( int i = 0; i < before.length(); i++ )
1622 {
1623 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1624 }
1625
1626 return QVariant( str );
1627 }
1628 else
1629 {
1630 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1631 return QVariant();
1632 }
1633}
1634
1635static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1636{
1637 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1638 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1639 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1640
1641 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1642 if ( !re.isValid() )
1643 {
1644 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1645 return QVariant();
1646 }
1647 return QVariant( str.replace( re, after ) );
1648}
1649
1650static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1651{
1652 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1653 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1654
1655 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1656 if ( !re.isValid() )
1657 {
1658 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1659 return QVariant();
1660 }
1661 return QVariant( ( str.indexOf( re ) + 1 ) );
1662}
1663
1664static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1665{
1666 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1667 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1668 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1669
1670 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1671 if ( !re.isValid() )
1672 {
1673 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1674 return QVariant();
1675 }
1676
1677 QRegularExpressionMatch matches = re.match( str );
1678 if ( matches.hasMatch() )
1679 {
1680 QVariantList array;
1681 QStringList list = matches.capturedTexts();
1682
1683 // Skip the first string to only return captured groups
1684 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1685 {
1686 array += ( !( *it ).isEmpty() ) ? *it : empty;
1687 }
1688
1689 return QVariant( array );
1690 }
1691 else
1692 {
1693 return QVariant();
1694 }
1695}
1696
1697static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1698{
1699 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1700 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1701
1702 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1703 if ( !re.isValid() )
1704 {
1705 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1706 return QVariant();
1707 }
1708
1709 // extract substring
1710 QRegularExpressionMatch match = re.match( str );
1711 if ( match.hasMatch() )
1712 {
1713 // return first capture
1714 if ( match.lastCapturedIndex() > 0 )
1715 {
1716 // a capture group was present, so use that
1717 return QVariant( match.captured( 1 ) );
1718 }
1719 else
1720 {
1721 // no capture group, so using all match
1722 return QVariant( match.captured( 0 ) );
1723 }
1724 }
1725 else
1726 {
1727 return QVariant( "" );
1728 }
1729}
1730
1731static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1732{
1733 QString uuid = QUuid::createUuid().toString();
1734 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1735 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1736 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1737 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1738 return uuid;
1739}
1740
1741static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1742{
1743 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1744 return QVariant();
1745
1746 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1747 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1748
1749 int len = 0;
1750 if ( values.at( 2 ).isValid() )
1751 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1752 else
1753 len = str.size();
1754
1755 if ( from < 0 )
1756 {
1757 from = str.size() + from;
1758 if ( from < 0 )
1759 {
1760 from = 0;
1761 }
1762 }
1763 else if ( from > 0 )
1764 {
1765 //account for the fact that substr() starts at 1
1766 from -= 1;
1767 }
1768
1769 if ( len < 0 )
1770 {
1771 len = str.size() + len - from;
1772 if ( len < 0 )
1773 {
1774 len = 0;
1775 }
1776 }
1777
1778 return QVariant( str.mid( from, len ) );
1779}
1780static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1781{
1782 FEAT_FROM_CONTEXT( context, f )
1783 return QVariant( f.id() );
1784}
1785
1786static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1787{
1788 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1789 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1790 bool foundLayer = false;
1791 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1792 {
1793 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1794 if ( !layer || !layer->dataProvider() )
1795 {
1796 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1797 return QVariant();
1798 }
1799
1800 if ( bandNb < 1 || bandNb > layer->bandCount() )
1801 {
1802 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1803 return QVariant();
1804 }
1805
1806 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1807 {
1808 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1809 return QVariant();
1810 }
1811
1812 QgsPointXY point = geom.asPoint();
1813 if ( geom.isMultipart() )
1814 {
1815 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1816 if ( multiPoint.count() == 1 )
1817 {
1818 point = multiPoint[0];
1819 }
1820 else
1821 {
1822 // if the geometry contains more than one part, return an undefined value
1823 return QVariant();
1824 }
1825 }
1826
1827 double value = layer->dataProvider()->sample( point, bandNb );
1828 return std::isnan( value ) ? QVariant() : value;
1829 },
1830 foundLayer );
1831
1832 if ( !foundLayer )
1833 {
1834 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1835 return QVariant();
1836 }
1837 else
1838 {
1839 return res;
1840 }
1841}
1842
1843static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1844{
1845 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1846 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1847
1848 bool foundLayer = false;
1849 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1850 {
1851 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1852 if ( !layer || !layer->dataProvider() )
1853 {
1854 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1855 return QVariant();
1856 }
1857
1858 if ( bandNb < 1 || bandNb > layer->bandCount() )
1859 {
1860 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1861 return QVariant();
1862 }
1863
1864 if ( std::isnan( value ) )
1865 {
1866 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1867 return QVariant();
1868 }
1869
1870 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1871 {
1872 return QVariant();
1873 }
1874
1875 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1876 if ( data.isEmpty() )
1877 {
1878 return QVariant();
1879 }
1880
1881 QVariantMap result;
1882 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1883 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1884 {
1885 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1886 if ( field.isColor() || field.isRamp() )
1887 {
1888 continue;
1889 }
1890 result.insert( fields.at( idx ).name, data.at( idx ) );
1891 }
1892
1893 return result;
1894 }, foundLayer );
1895
1896 if ( !foundLayer )
1897 {
1898 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1899 return QVariant();
1900 }
1901 else
1902 {
1903 return res;
1904 }
1905}
1906
1907static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1908{
1909 if ( !context )
1910 return QVariant();
1911
1912 return context->feature();
1913}
1914
1915static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1916{
1917 QgsFeature feature;
1918 QString attr;
1919 if ( values.size() == 1 )
1920 {
1921 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1922 feature = context->feature();
1923 }
1924 else if ( values.size() == 2 )
1925 {
1926 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1927 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1928 }
1929 else
1930 {
1931 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1932 return QVariant();
1933 }
1934
1935 return feature.attribute( attr );
1936}
1937
1938static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1939{
1940 QString table { R"html(
1941 <table>
1942 <thead>
1943 <tr><th>%1</th></tr>
1944 </thead>
1945 <tbody>
1946 <tr><td>%2</td></tr>
1947 </tbody>
1948 </table>)html" };
1949 QVariantMap dict;
1950 if ( values.size() == 1 )
1951 {
1952 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1953 }
1954 else
1955 {
1956 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1957 return QVariant();
1958 }
1959
1960 if ( dict.isEmpty() )
1961 {
1962 return QVariant();
1963 }
1964
1965 QStringList headers;
1966 QStringList cells;
1967
1968 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1969 {
1970 headers.push_back( it.key().toHtmlEscaped() );
1971 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1972 }
1973
1974 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1975}
1976
1977static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1978{
1979 QString table { R"html(
1980 <dl>
1981 %1
1982 </dl>)html" };
1983 QVariantMap dict;
1984 if ( values.size() == 1 )
1985 {
1986 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1987 }
1988 else
1989 {
1990 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1991 return QVariant();
1992 }
1993
1994 if ( dict.isEmpty() )
1995 {
1996 return QVariant();
1997 }
1998
1999 QString rows;
2000
2001 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2002 {
2003 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2004 }
2005
2006 return table.arg( rows );
2007}
2008
2009static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2010{
2011 QVariant layer;
2012 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2013 {
2014 layer = context->variable( QStringLiteral( "layer" ) );
2015 }
2016 else
2017 {
2018 //first node is layer id or name
2019 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2021 layer = node->eval( parent, context );
2023 }
2024
2025 QgsFeature feature;
2026 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2027 {
2028 feature = context->feature();
2029 }
2030 else
2031 {
2032 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2033 }
2034
2036 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2037 if ( strength == QLatin1String( "hard" ) )
2038 {
2040 }
2041 else if ( strength == QLatin1String( "soft" ) )
2042 {
2044 }
2045
2046 bool foundLayer = false;
2047 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2048 {
2049 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2050 if ( !layer )
2051 {
2052 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2053 return QVariant();
2054 }
2055
2056 const QgsFields fields = layer->fields();
2057 bool valid = true;
2058 for ( int i = 0; i < fields.size(); i++ )
2059 {
2060 QStringList errors;
2061 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2062 if ( !valid )
2063 {
2064 break;
2065 }
2066 }
2067
2068 return valid;
2069 }, foundLayer );
2070
2071 if ( !foundLayer )
2072 {
2073 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2074 return QVariant();
2075 }
2076
2077 return res;
2078}
2079
2080static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2081{
2082 QVariant layer;
2083 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2084 {
2085 layer = context->variable( QStringLiteral( "layer" ) );
2086 }
2087 else
2088 {
2089 //first node is layer id or name
2090 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2092 layer = node->eval( parent, context );
2094 }
2095
2096 QgsFeature feature;
2097 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2098 {
2099 feature = context->feature();
2100 }
2101 else
2102 {
2103 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2104 }
2105
2107 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2108 if ( strength == QLatin1String( "hard" ) )
2109 {
2111 }
2112 else if ( strength == QLatin1String( "soft" ) )
2113 {
2115 }
2116
2117 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2118
2119 bool foundLayer = false;
2120 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2121 {
2122 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2123 if ( !layer )
2124 {
2125 return QVariant();
2126 }
2127
2128 const int fieldIndex = layer->fields().indexFromName( attributeName );
2129 if ( fieldIndex == -1 )
2130 {
2131 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2132 return QVariant();
2133 }
2134
2135 QStringList errors;
2136 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2137 return valid;
2138 }, foundLayer );
2139
2140 if ( !foundLayer )
2141 {
2142 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2143 return QVariant();
2144 }
2145
2146 return res;
2147}
2148
2149static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2150{
2151 QgsFeature feature;
2152 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2153 {
2154 feature = context->feature();
2155 }
2156 else
2157 {
2158 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2159 }
2160
2161 const QgsFields fields = feature.fields();
2162 QVariantMap result;
2163 for ( int i = 0; i < fields.count(); ++i )
2164 {
2165 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2166 }
2167 return result;
2168}
2169
2170static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2171{
2172 QgsVectorLayer *layer = nullptr;
2173 QgsFeature feature;
2174
2175 // TODO this expression function is NOT thread safe
2177 if ( values.isEmpty() )
2178 {
2179 feature = context->feature();
2180 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2181 }
2182 else if ( values.size() == 1 )
2183 {
2184 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2185 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2186 }
2187 else if ( values.size() == 2 )
2188 {
2189 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2190 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2191 }
2192 else
2193 {
2194 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2195 return QVariant();
2196 }
2198
2199 if ( !layer )
2200 {
2201 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2202 return QVariant();
2203 }
2204
2205 if ( !feature.isValid() )
2206 {
2207 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2208 return QVariant();
2209 }
2210
2211 const QgsFields fields = feature.fields();
2212 QVariantMap result;
2213 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2214 {
2215 const QString fieldName { fields.at( fieldIndex ).name() };
2216 const QVariant attributeVal = feature.attribute( fieldIndex );
2217 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2218 if ( context && context->hasCachedValue( cacheValueKey ) )
2219 {
2220 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2221 }
2222 else
2223 {
2224 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2226 QVariant cache;
2227 if ( context )
2228 {
2229 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2230
2231 if ( !context->hasCachedValue( cacheKey ) )
2232 {
2233 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2234 context->setCachedValue( cacheKey, cache );
2235 }
2236 else
2237 {
2238 cache = context->cachedValue( cacheKey );
2239 }
2240 }
2241 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2242
2243 result.insert( fields.at( fieldIndex ).name(), value );
2244
2245 if ( context )
2246 {
2247 context->setCachedValue( cacheValueKey, value );
2248 }
2249
2250 }
2251 }
2252 return result;
2253}
2254
2255static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2256{
2257 QgsVectorLayer *layer = nullptr;
2258 QgsFeature feature;
2259 bool evaluate = true;
2260
2261 // TODO this expression function is NOT thread safe
2263 if ( values.isEmpty() )
2264 {
2265 feature = context->feature();
2266 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2267 }
2268 else if ( values.size() == 1 )
2269 {
2270 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2271 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2272 }
2273 else if ( values.size() == 2 )
2274 {
2275 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2276 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2277 }
2278 else if ( values.size() == 3 )
2279 {
2280 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2281 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2282 evaluate = values.value( 2 ).toBool();
2283 }
2284 else
2285 {
2286 if ( isMaptip )
2287 {
2288 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2289 }
2290 else
2291 {
2292 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2293 }
2294 return QVariant();
2295 }
2296
2297 if ( !layer )
2298 {
2299 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2300 return QVariant( );
2301 }
2303
2304 if ( !feature.isValid() )
2305 {
2306 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2307 return QVariant( );
2308 }
2309
2310 if ( ! evaluate )
2311 {
2312 if ( isMaptip )
2313 {
2314 return layer->mapTipTemplate();
2315 }
2316 else
2317 {
2318 return layer->displayExpression();
2319 }
2320 }
2321
2322 QgsExpressionContext subContext( *context );
2323 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2324 subContext.setFeature( feature );
2325
2326 if ( isMaptip )
2327 {
2328 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2329 }
2330 else
2331 {
2332 QgsExpression exp( layer->displayExpression() );
2333 exp.prepare( &subContext );
2334 return exp.evaluate( &subContext ).toString();
2335 }
2336}
2337
2338static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2339{
2340 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2341}
2342
2343static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2344{
2345 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2346}
2347
2348static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2349{
2350 QgsFeature feature;
2351 QVariant layer;
2352 if ( values.isEmpty() )
2353 {
2354 feature = context->feature();
2355 layer = context->variable( QStringLiteral( "layer" ) );
2356 }
2357 else if ( values.size() == 1 )
2358 {
2359 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2360 layer = context->variable( QStringLiteral( "layer" ) );
2361 }
2362 else if ( values.size() == 2 )
2363 {
2364 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2365 layer = values.at( 0 );
2366 }
2367 else
2368 {
2369 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2370 return QVariant();
2371 }
2372
2373 bool foundLayer = false;
2374 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2375 {
2376 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2377 if ( !layer || !feature.isValid() )
2378 {
2379 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2380 }
2381
2382 return layer->selectedFeatureIds().contains( feature.id() );
2383 }, foundLayer );
2384 if ( !foundLayer )
2385 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2386 else
2387 return res;
2388}
2389
2390static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2391{
2392 QVariant layer;
2393
2394 if ( values.isEmpty() )
2395 layer = context->variable( QStringLiteral( "layer" ) );
2396 else if ( values.count() == 1 )
2397 layer = values.at( 0 );
2398 else
2399 {
2400 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2401 return QVariant();
2402 }
2403
2404 bool foundLayer = false;
2405 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2406 {
2407 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2408 if ( !layer )
2409 {
2410 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2411 }
2412
2413 return layer->selectedFeatureCount();
2414 }, foundLayer );
2415 if ( !foundLayer )
2416 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2417 else
2418 return res;
2419}
2420
2421static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2422{
2423 static QMap<QString, qlonglong> counterCache;
2424 QVariant functionResult;
2425
2426 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2427 {
2428 QString database;
2429
2430 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2431
2432 if ( layer )
2433 {
2434 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2435 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2436 if ( database.isEmpty() )
2437 {
2438 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2439 }
2440 }
2441 else
2442 {
2443 database = databaseArgument;
2444 }
2445
2446 const QString table = values.at( 1 ).toString();
2447 const QString idColumn = values.at( 2 ).toString();
2448 const QString filterAttribute = values.at( 3 ).toString();
2449 const QVariant filterValue = values.at( 4 ).toString();
2450 const QVariantMap defaultValues = values.at( 5 ).toMap();
2451
2452 // read from database
2454 sqlite3_statement_unique_ptr sqliteStatement;
2455
2456 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2457 {
2458 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2459 functionResult = QVariant();
2460 return;
2461 }
2462
2463 QString errorMessage;
2464 QString currentValSql;
2465
2466 qlonglong nextId = 0;
2467 bool cachedMode = false;
2468 bool valueRetrieved = false;
2469
2470 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2471
2472 // Running in transaction mode, check for cached value first
2473 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2474 {
2475 cachedMode = true;
2476
2477 auto cachedCounter = counterCache.find( cacheString );
2478
2479 if ( cachedCounter != counterCache.end() )
2480 {
2481 qlonglong &cachedValue = cachedCounter.value();
2482 nextId = cachedValue;
2483 nextId += 1;
2484 cachedValue = nextId;
2485 valueRetrieved = true;
2486 }
2487 }
2488
2489 // Either not in cached mode or no cached value found, obtain from DB
2490 if ( !cachedMode || !valueRetrieved )
2491 {
2492 int result = SQLITE_ERROR;
2493
2494 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2495 if ( !filterAttribute.isNull() )
2496 {
2497 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2498 }
2499
2500 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2501
2502 if ( result == SQLITE_OK )
2503 {
2504 nextId = 0;
2505 if ( sqliteStatement.step() == SQLITE_ROW )
2506 {
2507 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2508 }
2509
2510 // If in cached mode: add value to cache and connect to transaction
2511 if ( cachedMode && result == SQLITE_OK )
2512 {
2513 counterCache.insert( cacheString, nextId );
2514
2515 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2516 {
2517 counterCache.remove( cacheString );
2518 } );
2519 }
2520 valueRetrieved = true;
2521 }
2522 }
2523
2524 if ( valueRetrieved )
2525 {
2526 QString upsertSql;
2527 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2528 QStringList cols;
2529 QStringList vals;
2530 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2531 vals << QgsSqliteUtils::quotedValue( nextId );
2532
2533 if ( !filterAttribute.isNull() )
2534 {
2535 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2536 vals << QgsSqliteUtils::quotedValue( filterValue );
2537 }
2538
2539 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2540 {
2541 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2542 vals << iter.value().toString();
2543 }
2544
2545 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2546 upsertSql += QLatin1String( " VALUES " );
2547 upsertSql += '(' + vals.join( ',' ) + ')';
2548
2549 int result = SQLITE_ERROR;
2550 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2551 {
2552 QgsTransaction *transaction = layer->dataProvider()->transaction();
2553 if ( transaction->executeSql( upsertSql, errorMessage ) )
2554 {
2555 result = SQLITE_OK;
2556 }
2557 }
2558 else
2559 {
2560 result = sqliteDb.exec( upsertSql, errorMessage );
2561 }
2562 if ( result == SQLITE_OK )
2563 {
2564 functionResult = QVariant( nextId );
2565 return;
2566 }
2567 else
2568 {
2569 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2570 functionResult = QVariant();
2571 return;
2572 }
2573 }
2574
2575 functionResult = QVariant();
2576 };
2577
2578 bool foundLayer = false;
2579 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2580 {
2581 fetchAndIncrementFunc( layer, QString() );
2582 }, foundLayer );
2583 if ( !foundLayer )
2584 {
2585 const QString databasePath = values.at( 0 ).toString();
2586 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2587 {
2588 fetchAndIncrementFunc( nullptr, databasePath );
2589 } );
2590 }
2591
2592 return functionResult;
2593}
2594
2595static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2596{
2597 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2598 if ( !crs.isValid() )
2599 return QVariant();
2600 return crs.authid();
2601}
2602
2603static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2604{
2605 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2606 QgsCoordinateReferenceSystem crs( definition );
2607
2608 if ( !crs.isValid() )
2609 {
2610 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to cordinate reference system" ).arg( definition ) );
2611 }
2612
2613 return QVariant::fromValue( crs );
2614}
2615
2616static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2617{
2618 QString concat;
2619 for ( const QVariant &value : values )
2620 {
2621 if ( !QgsVariantUtils::isNull( value ) )
2622 concat += QgsExpressionUtils::getStringValue( value, parent );
2623 }
2624 return concat;
2625}
2626
2627static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2628{
2629 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2630 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2631}
2632
2633static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2634{
2635 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2636 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2637 return string.right( pos );
2638}
2639
2640static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2641{
2642 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2643 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2644 return string.left( pos );
2645}
2646
2647static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2648{
2649 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2650 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2651 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2652 return string.leftJustified( length, fill.at( 0 ), true );
2653}
2654
2655static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2656{
2657 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2658 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2659 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2660 return string.rightJustified( length, fill.at( 0 ), true );
2661}
2662
2663static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2664{
2665 if ( values.size() < 1 )
2666 {
2667 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2668 return QVariant();
2669 }
2670
2671 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2672 for ( int n = 1; n < values.length(); n++ )
2673 {
2674 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2675 }
2676 return string;
2677}
2678
2679
2680static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2681{
2682 return QVariant( QDateTime::currentDateTime() );
2683}
2684
2685static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2686{
2687 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2688 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2689 if ( format.isEmpty() && !language.isEmpty() )
2690 {
2691 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2692 return QVariant( QDate() );
2693 }
2694
2695 if ( format.isEmpty() && language.isEmpty() )
2696 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2697
2698 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2699 QLocale locale = QLocale();
2700 if ( !language.isEmpty() )
2701 {
2702 locale = QLocale( language );
2703 }
2704
2705 QDate date = locale.toDate( datestring, format );
2706 if ( !date.isValid() )
2707 {
2708 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2709 date = QDate();
2710 }
2711 return QVariant( date );
2712}
2713
2714static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2715{
2716 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2717 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2718 if ( format.isEmpty() && !language.isEmpty() )
2719 {
2720 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2721 return QVariant( QTime() );
2722 }
2723
2724 if ( format.isEmpty() && language.isEmpty() )
2725 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2726
2727 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2728 QLocale locale = QLocale();
2729 if ( !language.isEmpty() )
2730 {
2731 locale = QLocale( language );
2732 }
2733
2734 QTime time = locale.toTime( timestring, format );
2735 if ( !time.isValid() )
2736 {
2737 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2738 time = QTime();
2739 }
2740 return QVariant( time );
2741}
2742
2743static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2744{
2745 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2746}
2747
2748/*
2749 * DMS functions
2750 */
2751
2752static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2753{
2754 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2755 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2756 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2757
2758 QString formatString;
2759 if ( values.count() > 3 )
2760 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2761
2763 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2764 {
2766 }
2767 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2768 {
2770 }
2771 else if ( ! formatString.isEmpty() )
2772 {
2773 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2774 return QVariant();
2775 }
2776
2777 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2778 {
2779 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2780 }
2781 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2782 {
2783 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2784 }
2785 else
2786 {
2787 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2788 return QVariant();
2789 }
2790}
2791
2792static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2793{
2795 return floatToDegreeFormat( format, values, context, parent, node );
2796}
2797
2798static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2799{
2800 double value = 0.0;
2801 bool ok = false;
2802 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2803
2804 return ok ? QVariant( value ) : QVariant();
2805}
2806
2807static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2808{
2810 return floatToDegreeFormat( format, values, context, parent, node );
2811}
2812
2813static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2814{
2815 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2816 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2817 qint64 seconds = d2.secsTo( d1 );
2818 return QVariant::fromValue( QgsInterval( seconds ) );
2819}
2820
2821static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2822{
2823 if ( !values.at( 0 ).canConvert<QDate>() )
2824 return QVariant();
2825
2826 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2827 if ( !date.isValid() )
2828 return QVariant();
2829
2830 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2831 // (to match PostgreSQL behavior)
2832 return date.dayOfWeek() % 7;
2833}
2834
2835static QVariant fcnDay( 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.days() );
2842 }
2843 else
2844 {
2845 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2846 return QVariant( d1.date().day() );
2847 }
2848}
2849
2850static QVariant fcnYear( 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.years() );
2857 }
2858 else
2859 {
2860 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2861 return QVariant( d1.date().year() );
2862 }
2863}
2864
2865static QVariant fcnMonth( 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.months() );
2872 }
2873 else
2874 {
2875 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2876 return QVariant( d1.date().month() );
2877 }
2878}
2879
2880static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2881{
2882 QVariant value = values.at( 0 );
2883 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2884 if ( inter.isValid() )
2885 {
2886 return QVariant( inter.weeks() );
2887 }
2888 else
2889 {
2890 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2891 return QVariant( d1.date().weekNumber() );
2892 }
2893}
2894
2895static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2896{
2897 QVariant value = values.at( 0 );
2898 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2899 if ( inter.isValid() )
2900 {
2901 return QVariant( inter.hours() );
2902 }
2903 else
2904 {
2905 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2906 return QVariant( t1.hour() );
2907 }
2908}
2909
2910static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2911{
2912 QVariant value = values.at( 0 );
2913 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2914 if ( inter.isValid() )
2915 {
2916 return QVariant( inter.minutes() );
2917 }
2918 else
2919 {
2920 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2921 return QVariant( t1.minute() );
2922 }
2923}
2924
2925static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2926{
2927 QVariant value = values.at( 0 );
2928 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2929 if ( inter.isValid() )
2930 {
2931 return QVariant( inter.seconds() );
2932 }
2933 else
2934 {
2935 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2936 return QVariant( t1.second() );
2937 }
2938}
2939
2940static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2941{
2942 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2943 if ( dt.isValid() )
2944 {
2945 return QVariant( dt.toMSecsSinceEpoch() );
2946 }
2947 else
2948 {
2949 return QVariant();
2950 }
2951}
2952
2953static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2954{
2955 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2956 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2957 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2958}
2959
2960static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2961{
2962 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2963 if ( parent->hasEvalError() )
2964 {
2965 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2966 return QVariant();
2967 }
2968 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2969 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2970}
2971
2972static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2974 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2975 if ( parent->hasEvalError() )
2976 {
2977 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2978 return QVariant();
2979 }
2980 bool ok;
2981 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2982}
2983
2984#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2985 if ( !(f).hasGeometry() ) \
2986 return QVariant(); \
2987 QgsGeometry g = (f).geometry(); \
2988 if ( (g).type() != (geomtype) ) \
2989 return QVariant();
2990
2991static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2992{
2993 FEAT_FROM_CONTEXT( context, f )
2995 if ( g.isMultipart() )
2996 {
2997 return g.asMultiPoint().at( 0 ).x();
2998 }
2999 else
3000 {
3001 return g.asPoint().x();
3002 }
3003}
3004
3005static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3006{
3007 FEAT_FROM_CONTEXT( context, f )
3009 if ( g.isMultipart() )
3010 {
3011 return g.asMultiPoint().at( 0 ).y();
3012 }
3013 else
3014 {
3015 return g.asPoint().y();
3016 }
3017}
3018
3019static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3020{
3021 FEAT_FROM_CONTEXT( context, f )
3023
3024 if ( g.isEmpty() )
3025 return QVariant();
3026
3027 const QgsAbstractGeometry *abGeom = g.constGet();
3028
3029 if ( g.isEmpty() || !abGeom->is3D() )
3030 return QVariant();
3031
3032 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3033 {
3034 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3035 if ( point )
3036 return point->z();
3037 }
3038 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3039 {
3040 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3041 {
3042 if ( collection->numGeometries() > 0 )
3043 {
3044 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3045 return point->z();
3046 }
3047 }
3048 }
3049
3050 return QVariant();
3051}
3052
3053static QVariant fcnGeomIsValid( 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 bool isValid = geom.isGeosValid();
3060
3061 return QVariant( isValid );
3062}
3063
3064static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3065{
3066 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3067 if ( geom.isNull() )
3068 return QVariant();
3069
3070 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3071#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3073#else
3075#endif
3076 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3078 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3080
3081 const bool keepCollapsed = values.value( 2 ).toBool();
3082
3083 QgsGeometry valid;
3084 try
3085 {
3086 valid = geom.makeValid( method, keepCollapsed );
3087 }
3088 catch ( QgsNotSupportedException & )
3089 {
3090 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3091 return QVariant();
3092 }
3093
3094 return QVariant::fromValue( valid );
3095}
3096
3097static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3098{
3099 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3100 if ( geom.isNull() )
3101 return QVariant();
3102
3103 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3104 QVariantList array;
3105 for ( int i = 0; i < multiGeom.size(); ++i )
3106 {
3107 array += QVariant::fromValue( multiGeom.at( i ) );
3108 }
3109
3110 return array;
3111}
3112
3113static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3114{
3115 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3116 if ( geom.isNull() )
3117 return QVariant();
3118
3119 //if single point, return the point's x coordinate
3120 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3121 {
3122 return geom.asPoint().x();
3123 }
3124
3125 //otherwise return centroid x
3126 QgsGeometry centroid = geom.centroid();
3127 QVariant result( centroid.asPoint().x() );
3128 return result;
3129}
3130
3131static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3132{
3133 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3134 if ( geom.isNull() )
3135 return QVariant();
3136
3137 //if single point, return the point's y coordinate
3138 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3139 {
3140 return geom.asPoint().y();
3141 }
3142
3143 //otherwise return centroid y
3144 QgsGeometry centroid = geom.centroid();
3145 QVariant result( centroid.asPoint().y() );
3146 return result;
3147}
3148
3149static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3150{
3151 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3152 if ( geom.isNull() )
3153 return QVariant(); //or 0?
3154
3155 if ( !geom.constGet()->is3D() )
3156 return QVariant();
3157
3158 //if single point, return the point's z coordinate
3159 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3160 {
3161 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3162 if ( point )
3163 return point->z();
3164 }
3165 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3166 {
3167 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3168 {
3169 if ( collection->numGeometries() == 1 )
3170 {
3171 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3172 return point->z();
3173 }
3174 }
3175 }
3176
3177 return QVariant();
3178}
3179
3180static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3181{
3182 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3183 if ( geom.isNull() )
3184 return QVariant(); //or 0?
3185
3186 if ( !geom.constGet()->isMeasure() )
3187 return QVariant();
3188
3189 //if single point, return the point's m value
3190 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3191 {
3192 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3193 if ( point )
3194 return point->m();
3195 }
3196 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3197 {
3198 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3199 {
3200 if ( collection->numGeometries() == 1 )
3201 {
3202 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3203 return point->m();
3204 }
3205 }
3206 }
3207
3208 return QVariant();
3209}
3210
3211static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3212{
3213 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3214
3215 if ( geom.isNull() )
3216 return QVariant();
3217
3218 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3219
3220 if ( idx < 0 )
3221 {
3222 //negative idx
3223 int count = geom.constGet()->nCoordinates();
3224 idx = count + idx;
3225 }
3226 else
3227 {
3228 //positive idx is 1 based
3229 idx -= 1;
3230 }
3231
3232 QgsVertexId vId;
3233 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3234 {
3235 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3236 return QVariant();
3237 }
3238
3239 QgsPoint point = geom.constGet()->vertexAt( vId );
3240 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3241}
3242
3243static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3244{
3245 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3246
3247 if ( geom.isNull() )
3248 return QVariant();
3249
3250 QgsVertexId vId;
3251 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3252 {
3253 return QVariant();
3254 }
3255
3256 QgsPoint point = geom.constGet()->vertexAt( vId );
3257 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3258}
3259
3260static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3261{
3262 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3263
3264 if ( geom.isNull() )
3265 return QVariant();
3266
3267 QgsVertexId vId;
3268 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3269 {
3270 return QVariant();
3271 }
3272
3273 QgsPoint point = geom.constGet()->vertexAt( vId );
3274 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3275}
3276
3277static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3278{
3279 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3280
3281 if ( geom.isNull() )
3282 return QVariant();
3283
3284 bool ignoreClosing = false;
3285 if ( values.length() > 1 )
3286 {
3287 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3288 }
3289
3290 QgsMultiPoint *mp = new QgsMultiPoint();
3291
3292 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3293 for ( const QgsRingSequence &part : sequence )
3294 {
3295 for ( const QgsPointSequence &ring : part )
3296 {
3297 bool skipLast = false;
3298 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3299 {
3300 skipLast = true;
3301 }
3302
3303 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3304 {
3305 mp->addGeometry( ring.at( i ).clone() );
3306 }
3307 }
3308 }
3309
3310 return QVariant::fromValue( QgsGeometry( mp ) );
3311}
3312
3313static QVariant fcnSegmentsToLines( 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 QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3321
3322 //OK, now we have a complete list of segmentized lines from the geometry
3324 for ( QgsLineString *line : linesToProcess )
3325 {
3326 for ( int i = 0; i < line->numPoints() - 1; ++i )
3327 {
3329 segment->setPoints( QgsPointSequence()
3330 << line->pointN( i )
3331 << line->pointN( i + 1 ) );
3332 ml->addGeometry( segment );
3333 }
3334 delete line;
3335 }
3336
3337 return QVariant::fromValue( QgsGeometry( ml ) );
3338}
3339
3340static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3341{
3342 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3343
3344 if ( geom.isNull() )
3345 return QVariant();
3346
3347 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3348 if ( !curvePolygon && geom.isMultipart() )
3349 {
3350 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3351 {
3352 if ( collection->numGeometries() == 1 )
3353 {
3354 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3355 }
3356 }
3357 }
3358
3359 if ( !curvePolygon )
3360 return QVariant();
3361
3362 //idx is 1 based
3363 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3364
3365 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3366 return QVariant();
3367
3368 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3369 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3370 return result;
3371}
3372
3373static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3374{
3375 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3376
3377 if ( geom.isNull() )
3378 return QVariant();
3379
3380 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3381 if ( !collection )
3382 return QVariant();
3383
3384 //idx is 1 based
3385 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3386
3387 if ( idx < 0 || idx >= collection->numGeometries() )
3388 return QVariant();
3389
3390 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3391 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3392 return result;
3393}
3394
3395static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3396{
3397 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3398
3399 if ( geom.isNull() )
3400 return QVariant();
3401
3402 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3403 if ( !boundary )
3404 return QVariant();
3405
3406 return QVariant::fromValue( QgsGeometry( boundary ) );
3407}
3408
3409static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3410{
3411 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3412
3413 if ( geom.isNull() )
3414 return QVariant();
3415
3416 QgsGeometry merged = geom.mergeLines();
3417 if ( merged.isNull() )
3418 return QVariant();
3419
3420 return QVariant::fromValue( merged );
3421}
3422
3423static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3424{
3425 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3426 if ( geom.isNull() )
3427 return QVariant();
3428
3429 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3430 if ( geom2.isNull() )
3431 return QVariant();
3432
3433 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3434 if ( sharedPaths.isNull() )
3435 return QVariant();
3436
3437 return QVariant::fromValue( sharedPaths );
3438}
3439
3440
3441static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3442{
3443 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3444
3445 if ( geom.isNull() )
3446 return QVariant();
3447
3448 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3449
3450 QgsGeometry simplified = geom.simplify( tolerance );
3451 if ( simplified.isNull() )
3452 return QVariant();
3453
3454 return simplified;
3455}
3456
3457static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3458{
3459 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3460
3461 if ( geom.isNull() )
3462 return QVariant();
3463
3464 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3465
3467
3468 QgsGeometry simplified = simplifier.simplify( geom );
3469 if ( simplified.isNull() )
3470 return QVariant();
3471
3472 return simplified;
3473}
3474
3475static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3476{
3477 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3478
3479 if ( geom.isNull() )
3480 return QVariant();
3481
3482 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3483 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3484 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3485 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3486
3487 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3488 if ( smoothed.isNull() )
3489 return QVariant();
3490
3491 return smoothed;
3492}
3493
3494static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3495{
3496 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3497
3498 if ( geom.isNull() )
3499 return QVariant();
3500
3501 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3502 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3503 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3504
3505 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3506 if ( waved.isNull() )
3507 return QVariant();
3508
3509 return waved;
3510}
3511
3512static QVariant fcnTriangularWaveRandomized( 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 minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3520 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3521 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3522 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3523 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3524
3525 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3526 minAmplitude, maxAmplitude, seed );
3527 if ( waved.isNull() )
3528 return QVariant();
3529
3530 return waved;
3531}
3532
3533static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3534{
3535 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3536
3537 if ( geom.isNull() )
3538 return QVariant();
3539
3540 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3541 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3542 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3543
3544 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3545 if ( waved.isNull() )
3546 return QVariant();
3547
3548 return waved;
3549}
3550
3551static QVariant fcnSquareWaveRandomized( 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 double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3559 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3560 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3561 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3562 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3563
3564 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3565 minAmplitude, maxAmplitude, seed );
3566 if ( waved.isNull() )
3567 return QVariant();
3568
3569 return waved;
3570}
3571
3572static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3573{
3574 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3575
3576 if ( geom.isNull() )
3577 return QVariant();
3578
3579 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3580 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3581 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3582
3583 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3584 if ( waved.isNull() )
3585 return QVariant();
3586
3587 return waved;
3588}
3589
3590static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3591{
3592 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3593
3594 if ( geom.isNull() )
3595 return QVariant();
3596
3597 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3598 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3599 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3600 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3601 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3602
3603 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3604 minAmplitude, maxAmplitude, seed );
3605 if ( waved.isNull() )
3606 return QVariant();
3607
3608 return waved;
3609}
3610
3611static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3612{
3613 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3614
3615 if ( geom.isNull() )
3616 return QVariant();
3617
3618 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3619 QVector< double > dashPattern;
3620 dashPattern.reserve( pattern.size() );
3621 for ( const QVariant &value : std::as_const( pattern ) )
3622 {
3623 bool ok = false;
3624 double v = value.toDouble( &ok );
3625 if ( ok )
3626 {
3627 dashPattern << v;
3628 }
3629 else
3630 {
3631 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3632 return QgsGeometry();
3633 }
3634 }
3635
3636 if ( dashPattern.size() % 2 != 0 )
3637 {
3638 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3639 return QgsGeometry();
3640 }
3641
3642 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3644 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3646 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3648 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3650 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3652 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3654 else
3655 {
3656 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3657 return QgsGeometry();
3658 }
3659
3660 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3662 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3664 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3666 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3668 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3670 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3672 else
3673 {
3674 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3675 return QgsGeometry();
3676 }
3677
3678 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3680 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3682 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3684 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3686 else
3687 {
3688 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3689 return QgsGeometry();
3690 }
3691
3692 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3693
3694 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3695 if ( result.isNull() )
3696 return QVariant();
3697
3698 return result;
3699}
3700
3701static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3702{
3703 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3704
3705 if ( geom.isNull() )
3706 return QVariant();
3707
3708 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3709 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3710 if ( densified.isNull() )
3711 return QVariant();
3712
3713 return densified;
3714}
3715
3716static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3717{
3718 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3719
3720 if ( geom.isNull() )
3721 return QVariant();
3722
3723 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3724 const QgsGeometry densified = geom.densifyByDistance( distance );
3725 if ( densified.isNull() )
3726 return QVariant();
3727
3728 return densified;
3729}
3730
3731static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3732{
3733 QVariantList list;
3734 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3735 {
3736 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3737 }
3738 else
3739 {
3740 list = values;
3741 }
3742
3743 QVector< QgsGeometry > parts;
3744 parts.reserve( list.size() );
3745 for ( const QVariant &value : std::as_const( list ) )
3746 {
3747 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
3748 if ( part.isNull() )
3749 return QgsGeometry();
3750 parts << part;
3751 }
3752
3753 return QgsGeometry::collectGeometry( parts );
3754}
3755
3756static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3757{
3758 if ( values.count() < 2 || values.count() > 4 )
3759 {
3760 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3761 return QVariant();
3762 }
3763
3764 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3765 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3766 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3767 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3768 switch ( values.count() )
3769 {
3770 case 2:
3771 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3772 case 3:
3773 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3774 case 4:
3775 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3776 }
3777 return QVariant(); //avoid warning
3778}
3779
3780static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3781{
3782 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3783 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3784 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3785 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3786}
3787
3788static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3789{
3790 if ( values.empty() )
3791 {
3792 return QVariant();
3793 }
3794
3795 QVector<QgsPoint> points;
3796 points.reserve( values.count() );
3797
3798 auto addPoint = [&points]( const QgsGeometry & geom )
3799 {
3800 if ( geom.isNull() )
3801 return;
3802
3803 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3804 return;
3805
3806 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3807 if ( !point )
3808 return;
3809
3810 points << *point;
3811 };
3812
3813 for ( const QVariant &value : values )
3814 {
3815 if ( value.userType() == QMetaType::Type::QVariantList )
3816 {
3817 const QVariantList list = value.toList();
3818 for ( const QVariant &v : list )
3819 {
3820 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3821 }
3822 }
3823 else
3824 {
3825 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3826 }
3827 }
3828
3829 if ( points.count() < 2 )
3830 return QVariant();
3831
3832 return QgsGeometry( new QgsLineString( points ) );
3833}
3834
3835static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3836{
3837 if ( values.count() < 1 )
3838 {
3839 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3840 return QVariant();
3841 }
3842
3843 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3844
3845 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3846 return outerRing; // if it's already a polygon we have nothing to do
3847
3848 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3849 return QVariant();
3850
3851 auto polygon = std::make_unique< QgsPolygon >();
3852
3853 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
3854 if ( !exteriorRing && outerRing.isMultipart() )
3855 {
3856 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3857 {
3858 if ( collection->numGeometries() == 1 )
3859 {
3860 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3861 }
3862 }
3863 }
3864
3865 if ( !exteriorRing )
3866 return QVariant();
3867
3868 polygon->setExteriorRing( exteriorRing->segmentize() );
3869
3870
3871 for ( int i = 1; i < values.count(); ++i )
3872 {
3873 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3874 if ( ringGeom.isNull() )
3875 continue;
3876
3877 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3878 continue;
3879
3880 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
3881 if ( !ring && ringGeom.isMultipart() )
3882 {
3883 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3884 {
3885 if ( collection->numGeometries() == 1 )
3886 {
3887 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3888 }
3889 }
3890 }
3891
3892 if ( !ring )
3893 continue;
3894
3895 polygon->addInteriorRing( ring->segmentize() );
3896 }
3897
3898 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3899}
3900
3901static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3902{
3903 auto tr = std::make_unique<QgsTriangle>();
3904 auto lineString = std::make_unique<QgsLineString>();
3905 lineString->clear();
3906
3907 for ( const QVariant &value : values )
3908 {
3909 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3910 if ( geom.isNull() )
3911 return QVariant();
3912
3913 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3914 return QVariant();
3915
3916 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3917 if ( !point && geom.isMultipart() )
3918 {
3919 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3920 {
3921 if ( collection->numGeometries() == 1 )
3922 {
3923 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3924 }
3925 }
3926 }
3927
3928 if ( !point )
3929 return QVariant();
3930
3931 lineString->addVertex( *point );
3932 }
3933
3934 tr->setExteriorRing( lineString.release() );
3935
3936 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3937}
3938
3939static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3940{
3941 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3942 if ( geom.isNull() )
3943 return QVariant();
3944
3945 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3946 return QVariant();
3947
3948 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3949 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3950
3951 if ( segment < 3 )
3952 {
3953 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3954 return QVariant();
3955 }
3956 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3957 if ( !point && geom.isMultipart() )
3958 {
3959 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3960 {
3961 if ( collection->numGeometries() == 1 )
3962 {
3963 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3964 }
3965 }
3966 }
3967 if ( !point )
3968 return QVariant();
3969
3970 QgsCircle circ( *point, radius );
3971 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3972}
3973
3974static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3975{
3976 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3977 if ( geom.isNull() )
3978 return QVariant();
3979
3980 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3981 return QVariant();
3982
3983 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3984 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3985 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3986 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3987 if ( segment < 3 )
3988 {
3989 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3990 return QVariant();
3991 }
3992 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3993 if ( !point && geom.isMultipart() )
3994 {
3995 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3996 {
3997 if ( collection->numGeometries() == 1 )
3998 {
3999 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4000 }
4001 }
4002 }
4003 if ( !point )
4004 return QVariant();
4005
4006 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4007 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4008}
4009
4010static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4011{
4012
4013 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4014 if ( pt1.isNull() )
4015 return QVariant();
4016
4017 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4018 return QVariant();
4019
4020 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4021 if ( pt2.isNull() )
4022 return QVariant();
4023
4024 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4025 return QVariant();
4026
4027 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4028 if ( nbEdges < 3 )
4029 {
4030 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4031 return QVariant();
4032 }
4033
4034 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4036 {
4037 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4038 return QVariant();
4039 }
4040
4041 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
4042 if ( !center && pt1.isMultipart() )
4043 {
4044 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
4045 {
4046 if ( collection->numGeometries() == 1 )
4047 {
4048 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4049 }
4050 }
4051 }
4052 if ( !center )
4053 return QVariant();
4054
4055 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
4056 if ( !corner && pt2.isMultipart() )
4057 {
4058 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
4059 {
4060 if ( collection->numGeometries() == 1 )
4061 {
4062 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4063 }
4064 }
4065 }
4066 if ( !corner )
4067 return QVariant();
4068
4069 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4070
4071 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4072
4073}
4074
4075static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4076{
4077 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4078 if ( pt1.isNull() )
4079 return QVariant();
4080 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4081 return QVariant();
4082
4083 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4084 if ( pt2.isNull() )
4085 return QVariant();
4086 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4087 return QVariant();
4088
4089 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4090 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4091 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4092
4093 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4094}
4095
4096static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4097{
4098 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4099 if ( pt1.isNull() )
4100 return QVariant();
4101 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4102 return QVariant();
4103
4104 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4105 if ( pt2.isNull() )
4106 return QVariant();
4107 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4108 return QVariant();
4109
4110 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4111 if ( pt3.isNull() )
4112 return QVariant();
4113 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4114 return QVariant();
4115
4116 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4117 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4118 {
4119 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4120 return QVariant();
4121 }
4122 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4123 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4124 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4125 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4126 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4127}
4128
4129static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4130{
4131 if ( geom.isNull() )
4132 return QVariant();
4133
4134 if ( idx < 0 )
4135 {
4136 idx += geom.constGet()->nCoordinates();
4137 }
4138 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4139 {
4140 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4141 return QVariant();
4142 }
4143 return QVariant::fromValue( geom.vertexAt( idx ) );
4144}
4145
4146// function used for the old $ style
4147static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4148{
4149 FEAT_FROM_CONTEXT( context, feature )
4150 const QgsGeometry geom = feature.geometry();
4151 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4152
4153 const QVariant v = pointAt( geom, idx, parent );
4154
4155 if ( !v.isNull() )
4156 return QVariant( v.value<QgsPoint>().x() );
4157 else
4158 return QVariant();
4159}
4160static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4161{
4162 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))
4163 {
4164 return fcnOldXat( values, f, parent, node );
4165 }
4166 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)
4167 {
4168 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4169 }
4170
4171 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4172 if ( geom.isNull() )
4173 {
4174 return QVariant();
4175 }
4176
4177 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4178
4179 const QVariant v = pointAt( geom, vertexNumber, parent );
4180 if ( !v.isNull() )
4181 return QVariant( v.value<QgsPoint>().x() );
4182 else
4183 return QVariant();
4184}
4185
4186// function used for the old $ style
4187static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4188{
4189 FEAT_FROM_CONTEXT( context, feature )
4190 const QgsGeometry geom = feature.geometry();
4191 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4192
4193 const QVariant v = pointAt( geom, idx, parent );
4194
4195 if ( !v.isNull() )
4196 return QVariant( v.value<QgsPoint>().y() );
4197 else
4198 return QVariant();
4199}
4200static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4201{
4202 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))
4203 {
4204 return fcnOldYat( values, f, parent, node );
4205 }
4206 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)
4207 {
4208 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4209 }
4210
4211 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4212 if ( geom.isNull() )
4213 {
4214 return QVariant();
4215 }
4216
4217 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4218
4219 const QVariant v = pointAt( geom, vertexNumber, parent );
4220 if ( !v.isNull() )
4221 return QVariant( v.value<QgsPoint>().y() );
4222 else
4223 return QVariant();
4224}
4225
4226static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4227{
4228 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4229 if ( geom.isNull() )
4230 {
4231 return QVariant();
4232 }
4233
4234 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4235
4236 const QVariant v = pointAt( geom, vertexNumber, parent );
4237 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4238 return QVariant( v.value<QgsPoint>().z() );
4239 else
4240 return QVariant();
4241}
4242
4243static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4244{
4245 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4246 if ( geom.isNull() )
4247 {
4248 return QVariant();
4249 }
4250
4251 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4252
4253 const QVariant v = pointAt( geom, vertexNumber, parent );
4254 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4255 return QVariant( v.value<QgsPoint>().m() );
4256 else
4257 return QVariant();
4258}
4259
4260
4261static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4262{
4263 if ( !context )
4264 return QVariant();
4265
4266 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4267 if ( context->hasGeometry() )
4268 return context->geometry();
4269 else
4270 {
4271 FEAT_FROM_CONTEXT( context, f )
4272 QgsGeometry geom = f.geometry();
4273 if ( !geom.isNull() )
4274 return QVariant::fromValue( geom );
4275 else
4276 return QVariant();
4277 }
4278}
4279
4280static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4281{
4282 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4283 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4284 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4285 return result;
4286}
4287
4288static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4289{
4290 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4291 if ( wkb.isNull() )
4292 return QVariant();
4293
4294 QgsGeometry geom;
4295 geom.fromWkb( wkb );
4296 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4297}
4298
4299static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4300{
4301 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4302 QgsOgcUtils::Context ogcContext;
4303 if ( context )
4304 {
4305 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4306 if ( mapLayerPtr )
4307 {
4308 ogcContext.layer = mapLayerPtr.data();
4309 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4310 }
4311 }
4312 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4313 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4314 return result;
4315}
4316
4317static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4318{
4319 FEAT_FROM_CONTEXT( context, f )
4321 QgsDistanceArea *calc = parent->geomCalculator();
4322 if ( calc )
4323 {
4324 try
4325 {
4326 double area = calc->measureArea( f.geometry() );
4327 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4328 return QVariant( area );
4329 }
4330 catch ( QgsCsException & )
4331 {
4332 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4333 return QVariant();
4334 }
4335 }
4336 else
4337 {
4338 return QVariant( f.geometry().area() );
4339 }
4340}
4341
4342static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4343{
4344 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4345
4346 if ( geom.type() != Qgis::GeometryType::Polygon )
4347 return QVariant();
4348
4349 return QVariant( geom.area() );
4350}
4351
4352static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4353{
4354 FEAT_FROM_CONTEXT( context, f )
4356 QgsDistanceArea *calc = parent->geomCalculator();
4357 if ( calc )
4358 {
4359 try
4360 {
4361 double len = calc->measureLength( f.geometry() );
4362 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4363 return QVariant( len );
4364 }
4365 catch ( QgsCsException & )
4366 {
4367 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4368 return QVariant();
4369 }
4370 }
4371 else
4372 {
4373 return QVariant( f.geometry().length() );
4374 }
4375}
4376
4377static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4378{
4379 FEAT_FROM_CONTEXT( context, f )
4381 QgsDistanceArea *calc = parent->geomCalculator();
4382 if ( calc )
4383 {
4384 try
4385 {
4386 double len = calc->measurePerimeter( f.geometry() );
4387 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4388 return QVariant( len );
4389 }
4390 catch ( QgsCsException & )
4391 {
4392 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4393 return QVariant();
4394 }
4395 }
4396 else
4397 {
4398 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4399 }
4400}
4401
4402static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4403{
4404 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4405
4406 if ( geom.type() != Qgis::GeometryType::Polygon )
4407 return QVariant();
4408
4409 //length for polygons = perimeter
4410 return QVariant( geom.length() );
4411}
4412
4413static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4414{
4415 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4416 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4417}
4418
4419static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4420{
4421 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4422 if ( geom.isNull() )
4423 return QVariant();
4424
4425 return QVariant( geom.constGet()->partCount() );
4426}
4427
4428static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4429{
4430 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4431 if ( geom.isNull() )
4432 return QVariant();
4433
4434 return QVariant( geom.isMultipart() );
4435}
4436
4437static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4438{
4439 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4440
4441 if ( geom.isNull() )
4442 return QVariant();
4443
4444 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4445 if ( curvePolygon )
4446 return QVariant( curvePolygon->numInteriorRings() );
4447
4448 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4449 if ( collection )
4450 {
4451 //find first CurvePolygon in collection
4452 for ( int i = 0; i < collection->numGeometries(); ++i )
4453 {
4454 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4455 if ( !curvePolygon )
4456 continue;
4457
4458 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4459 }
4460 }
4461
4462 return QVariant();
4463}
4464
4465static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4466{
4467 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4468
4469 if ( geom.isNull() )
4470 return QVariant();
4471
4472 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4473 if ( curvePolygon )
4474 return QVariant( curvePolygon->ringCount() );
4475
4476 bool foundPoly = false;
4477 int ringCount = 0;
4478 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4479 if ( collection )
4480 {
4481 //find CurvePolygons in collection
4482 for ( int i = 0; i < collection->numGeometries(); ++i )
4483 {
4484 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4485 if ( !curvePolygon )
4486 continue;
4487
4488 foundPoly = true;
4489 ringCount += curvePolygon->ringCount();
4490 }
4491 }
4492
4493 if ( !foundPoly )
4494 return QVariant();
4495
4496 return QVariant( ringCount );
4497}
4498
4499static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4500{
4501 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4502 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4503 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4504 return result;
4505}
4506
4507static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4508{
4509 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4510 return QVariant::fromValue( geom.boundingBox().width() );
4511}
4512
4513static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4514{
4515 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4516 return QVariant::fromValue( geom.boundingBox().height() );
4517}
4518
4519static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4520{
4521 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4522 if ( geom.isNull() )
4523 return QVariant();
4524
4526}
4527
4528static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4529{
4530 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4531 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4532}
4533
4534static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4535{
4536 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4537 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4538}
4539
4540static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4541{
4542 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4543 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4544}
4545
4546static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4547{
4548 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4549 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4550}
4551
4552static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4553{
4554 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4555
4556 if ( geom.isNull() || geom.isEmpty( ) )
4557 return QVariant();
4558
4559 if ( !geom.constGet()->is3D() )
4560 return QVariant();
4561
4562 double max = std::numeric_limits< double >::lowest();
4563
4564 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4565 {
4566 double z = ( *it ).z();
4567
4568 if ( max < z )
4569 max = z;
4570 }
4571
4572 if ( max == std::numeric_limits< double >::lowest() )
4573 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4574
4575 return QVariant( max );
4576}
4577
4578static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4579{
4580 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4581
4582 if ( geom.isNull() || geom.isEmpty() )
4583 return QVariant();
4584
4585 if ( !geom.constGet()->is3D() )
4586 return QVariant();
4587
4588 double min = std::numeric_limits< double >::max();
4589
4590 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4591 {
4592 double z = ( *it ).z();
4593
4594 if ( z < min )
4595 min = z;
4596 }
4597
4598 if ( min == std::numeric_limits< double >::max() )
4599 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4600
4601 return QVariant( min );
4602}
4603
4604static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4605{
4606 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4607
4608 if ( geom.isNull() || geom.isEmpty() )
4609 return QVariant();
4610
4611 if ( !geom.constGet()->isMeasure() )
4612 return QVariant();
4613
4614 double min = std::numeric_limits< double >::max();
4615
4616 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4617 {
4618 double m = ( *it ).m();
4619
4620 if ( m < min )
4621 min = m;
4622 }
4623
4624 if ( min == std::numeric_limits< double >::max() )
4625 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4626
4627 return QVariant( min );
4628}
4629
4630static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4631{
4632 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4633
4634 if ( geom.isNull() || geom.isEmpty() )
4635 return QVariant();
4636
4637 if ( !geom.constGet()->isMeasure() )
4638 return QVariant();
4639
4640 double max = std::numeric_limits< double >::lowest();
4641
4642 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4643 {
4644 double m = ( *it ).m();
4645
4646 if ( max < m )
4647 max = m;
4648 }
4649
4650 if ( max == std::numeric_limits< double >::lowest() )
4651 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4652
4653 return QVariant( max );
4654}
4655
4656static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4657{
4658 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4659 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4660 if ( !curve )
4661 {
4662 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4663 return QVariant();
4664 }
4665
4666 return QVariant( curve->sinuosity() );
4667}
4668
4669static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4670{
4671 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4672 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4673 if ( !curve )
4674 {
4675 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4676 return QVariant();
4677 }
4678
4679 return QVariant( curve->straightDistance2d() );
4680}
4681
4682static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4683{
4684 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4685 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4686
4687 if ( !poly )
4688 {
4689 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4690 return QVariant();
4691 }
4692
4693 return QVariant( poly->roundness() );
4694}
4695
4696
4697
4698static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4699{
4700 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4701 if ( geom.isNull() )
4702 return QVariant();
4703
4704 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4705 flipped->swapXy();
4706 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4707}
4708
4709static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4710{
4711 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4712 if ( fGeom.isNull() )
4713 return QVariant();
4714
4715 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4716 if ( !curve && fGeom.isMultipart() )
4717 {
4718 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4719 {
4720 if ( collection->numGeometries() == 1 )
4721 {
4722 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4723 }
4724 }
4725 }
4726
4727 if ( !curve )
4728 return QVariant();
4729
4730 return QVariant::fromValue( curve->isClosed() );
4731}
4732
4733static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4734{
4735 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4736
4737 if ( geom.isNull() )
4738 return QVariant();
4739
4740 QVariant result;
4741 if ( !geom.isMultipart() )
4742 {
4743 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4744
4745 if ( !line )
4746 return QVariant();
4747
4748 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4749 closedLine->close();
4750
4751 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4752 }
4753 else
4754 {
4755 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4756 if ( !collection )
4757 return QVariant();
4758
4759 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4760
4761 for ( int i = 0; i < collection->numGeometries(); ++i )
4762 {
4763 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4764 {
4765 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4766 closedLine->close();
4767
4768 closed->addGeometry( closedLine.release() );
4769 }
4770 }
4771 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4772 }
4773
4774 return result;
4775}
4776
4777static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4778{
4779 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4780 if ( fGeom.isNull() )
4781 return QVariant();
4782
4783 return QVariant::fromValue( fGeom.isEmpty() );
4784}
4785
4786static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4787{
4788 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4789 return QVariant::fromValue( true );
4790
4791 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4792 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4793}
4794
4795static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4796{
4797 if ( values.length() < 2 || values.length() > 3 )
4798 return QVariant();
4799
4800 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4801 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4802
4803 if ( fGeom.isNull() || sGeom.isNull() )
4804 return QVariant();
4805
4806 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4807
4808 if ( values.length() == 2 )
4809 {
4810 //two geometry arguments, return relation
4811 QString result = engine->relate( sGeom.constGet() );
4812 return QVariant::fromValue( result );
4813 }
4814 else
4815 {
4816 //three arguments, test pattern
4817 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4818 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4819 return QVariant::fromValue( result );
4820 }
4821}
4822
4823static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4824{
4825 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4826 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4827 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4828}
4829static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4830{
4831 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4832 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4833 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4834}
4835static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4836{
4837 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4838 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4839 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4840}
4841static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4842{
4843 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4844 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4845 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4846}
4847static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4848{
4849 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4850 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4851 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4852}
4853static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4854{
4855 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4856 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4857 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4858}
4859static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4860{
4861 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4862 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4863 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4864}
4865static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4866{
4867 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4868 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4869 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4870}
4871
4872static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4873{
4874 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4875 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4876 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4877 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4878 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4879 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4880
4882 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4883 capStyle = Qgis::EndCapStyle::Flat;
4884 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4885 capStyle = Qgis::EndCapStyle::Square;
4886
4888 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4889 joinStyle = Qgis::JoinStyle::Miter;
4890 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4891 joinStyle = Qgis::JoinStyle::Bevel;
4892
4893 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4894 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4895 return result;
4896}
4897
4898static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4899{
4900 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4901 const QgsGeometry reoriented = fGeom.forceRHR();
4902 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4903}
4904
4905static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4906{
4907 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4908 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4909 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4910}
4911
4912static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4913{
4914 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4915 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4916 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4917}
4918
4919static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4920{
4921 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4922 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4923 if ( !pt && fGeom.isMultipart() )
4924 {
4925 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4926 {
4927 if ( collection->numGeometries() == 1 )
4928 {
4929 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4930 }
4931 }
4932 }
4933
4934 if ( !pt )
4935 {
4936 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4937 return QVariant();
4938 }
4939
4940 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4941 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4942 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4943 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4944
4945 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4946 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4947 return result;
4948}
4949
4950static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4951{
4952 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4953 if ( fGeom.type() != Qgis::GeometryType::Line )
4954 {
4955 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4956 return QVariant();
4957 }
4958
4959 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4960 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4961 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4962
4963 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4964 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4965 return result;
4966}
4967
4968static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4969{
4970 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4971 if ( fGeom.type() != Qgis::GeometryType::Line )
4972 {
4973 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4974 return QVariant();
4975 }
4976
4977 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4978
4979 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4980 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4981 return result;
4982}
4983
4984static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4985{
4986 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4987 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4988 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4989 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4990 if ( joinInt < 1 || joinInt > 3 )
4991 return QVariant();
4992 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4993
4994 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4995
4996 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4997 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4998 return result;
4999}
5000
5001static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5002{
5003 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5004 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5005 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5006
5007 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5008 if ( joinInt < 1 || joinInt > 3 )
5009 return QVariant();
5010 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5011
5012 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5013
5014 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5015 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5016 return result;
5017}
5018
5019static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5020{
5021 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5022 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5023 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5024
5025 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5026 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5027 return result;
5028}
5029
5030static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5031{
5032 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5033 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5034 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5035 fGeom.translate( dx, dy );
5036 return QVariant::fromValue( fGeom );
5037}
5038
5039static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5040{
5041 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5042 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5043 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5044 : QgsGeometry();
5045 const bool perPart = values.value( 3 ).toBool();
5046
5047 if ( center.isNull() && perPart && fGeom.isMultipart() )
5048 {
5049 // no explicit center, rotating per part
5050 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5051 // the result is equivalent to setting perPart as false anyway)
5052 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5053 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5054 {
5055 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5056 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5057 t.rotate( -rotation );
5058 t.translate( -partCenter.x(), -partCenter.y() );
5059 ( *it )->transform( t );
5060 }
5061 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5062 }
5063 else
5064 {
5065 QgsPointXY pt;
5066 if ( center.isEmpty() )
5067 {
5068 // if center wasn't specified, use bounding box centroid
5069 pt = fGeom.boundingBox().center();
5070 }
5072 {
5073 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5074 return QVariant();
5075 }
5076 else
5077 {
5078 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
5079 }
5080
5081 fGeom.rotate( rotation, pt );
5082 return QVariant::fromValue( fGeom );
5083 }
5084}
5085
5086static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5087{
5088 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5089 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5090 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5091 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5092 : QgsGeometry();
5093
5094 QgsPointXY pt;
5095 if ( center.isNull() )
5096 {
5097 // if center wasn't specified, use bounding box centroid
5098 pt = fGeom.boundingBox().center();
5099 }
5101 {
5102 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5103 return QVariant();
5104 }
5105 else
5106 {
5107 pt = center.asPoint();
5108 }
5109
5110 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5111 t.scale( xScale, yScale );
5112 t.translate( -pt.x(), -pt.y() );
5113 fGeom.transform( t );
5114 return QVariant::fromValue( fGeom );
5115}
5116
5117static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5118{
5119 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5120 if ( fGeom.isNull() )
5121 {
5122 return QVariant();
5123 }
5124
5125 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5126 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5127
5128 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5129
5130 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5131 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5132
5133 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5134 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5135 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5136 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5137
5138 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5139 {
5140 fGeom.get()->addZValue( 0 );
5141 }
5142 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5143 {
5144 fGeom.get()->addMValue( 0 );
5145 }
5146
5147 QTransform transform;
5148 transform.translate( deltaX, deltaY );
5149 transform.rotate( rotationZ );
5150 transform.scale( scaleX, scaleY );
5151 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5152
5153 return QVariant::fromValue( fGeom );
5154}
5155
5156
5157static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5158{
5159 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5160 QgsGeometry geom = fGeom.centroid();
5161 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5162 return result;
5163}
5164static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5165{
5166 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5167 QgsGeometry geom = fGeom.pointOnSurface();
5168 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5169 return result;
5170}
5171
5172static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5173{
5174 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5175 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5176 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5177 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5178 return result;
5179}
5180
5181static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5182{
5183 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5184 QgsGeometry geom = fGeom.convexHull();
5185 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5186 return result;
5187}
5188
5189#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5190static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5191{
5192 try
5193 {
5194 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5195 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5196 const bool allowHoles = values.value( 2 ).toBool();
5197 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5198 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5199 return result;
5200 }
5201 catch ( QgsCsException &cse )
5202 {
5203 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5204 return QVariant();
5205 }
5206}
5207#endif
5208
5209static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5210{
5211 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5212 int segments = 36;
5213 if ( values.length() == 2 )
5214 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5215 if ( segments < 0 )
5216 {
5217 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5218 return QVariant();
5219 }
5220
5221 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5222 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5223 return result;
5224}
5225
5226static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5227{
5228 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5230 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5231 return result;
5232}
5233
5234static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5235{
5236 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5237
5238 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5239 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5240 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5241
5242 double area, angle, width, height;
5243 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5244
5245 if ( geom.isNull() )
5246 {
5247 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5248 return QVariant();
5249 }
5250 return angle;
5251}
5252
5253static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5254{
5255 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5256 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5257 QgsGeometry geom = fGeom.difference( sGeom );
5258 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5259 return result;
5260}
5261
5262static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5263{
5264 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5265 return QVariant();
5266
5267 // two variants, one for geometry, one for string
5268
5269 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5270 if ( !fGeom.isNull() )
5271 {
5272 QVariant result;
5273 if ( !fGeom.isMultipart() )
5274 {
5275 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5276 if ( !curve )
5277 return QVariant();
5278
5279 QgsCurve *reversed = curve->reversed();
5280 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5281 }
5282 else
5283 {
5284 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5285 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5286 for ( int i = 0; i < collection->numGeometries(); ++i )
5287 {
5288 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5289 {
5290 reversed->addGeometry( curve->reversed() );
5291 }
5292 else
5293 {
5294 reversed->addGeometry( collection->geometryN( i )->clone() );
5295 }
5296 }
5297 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5298 }
5299 return result;
5300 }
5301
5302 //fall back to string variant
5303 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5304 std::reverse( string.begin(), string.end() );
5305 return string;
5306}
5307
5308static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5309{
5310 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5311 if ( fGeom.isNull() )
5312 return QVariant();
5313
5314 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5315 if ( !curvePolygon && fGeom.isMultipart() )
5316 {
5317 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5318 {
5319 if ( collection->numGeometries() == 1 )
5320 {
5321 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5322 }
5323 }
5324 }
5325
5326 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5327 return QVariant();
5328
5329 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5330 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5331 return result;
5332}
5333
5334static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5335{
5336 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5337 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5338 return QVariant( fGeom.distance( sGeom ) );
5339}
5340
5341static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5342{
5343 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5344 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5345
5346 double res = -1;
5347 if ( values.length() == 3 && values.at( 2 ).isValid() )
5348 {
5349 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5350 densify = std::clamp( densify, 0.0, 1.0 );
5351 res = g1.hausdorffDistanceDensify( g2, densify );
5352 }
5353 else
5354 {
5355 res = g1.hausdorffDistance( g2 );
5356 }
5357
5358 return res > -1 ? QVariant( res ) : QVariant();
5359}
5360
5361static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5362{
5363 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5364 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5365 QgsGeometry geom = fGeom.intersection( sGeom );
5366 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5367 return result;
5368}
5369static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5370{
5371 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5372 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5373 QgsGeometry geom = fGeom.symDifference( sGeom );
5374 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5375 return result;
5376}
5377static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5378{
5379 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5380 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5381 QgsGeometry geom = fGeom.combine( sGeom );
5382 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5383 return result;
5384}
5385
5386static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5387{
5388 if ( values.length() < 1 || values.length() > 2 )
5389 return QVariant();
5390
5391 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5392 int prec = 8;
5393 if ( values.length() == 2 )
5394 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5395 QString wkt = fGeom.asWkt( prec );
5396 return QVariant( wkt );
5397}
5398
5399static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5400{
5401 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5402 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5403}
5404
5405static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5406{
5407 if ( values.length() != 2 )
5408 {
5409 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5410 return QVariant();
5411 }
5412
5413 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5414 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5415
5416 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5417 if ( !pt1 && fGeom1.isMultipart() )
5418 {
5419 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5420 {
5421 if ( collection->numGeometries() == 1 )
5422 {
5423 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5424 }
5425 }
5426 }
5427
5428 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5429 if ( !pt2 && fGeom2.isMultipart() )
5430 {
5431 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5432 {
5433 if ( collection->numGeometries() == 1 )
5434 {
5435 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5436 }
5437 }
5438 }
5439
5440 if ( !pt1 || !pt2 )
5441 {
5442 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5443 return QVariant();
5444 }
5445
5446 // Code from PostGIS
5447 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5448 {
5449 if ( pt1->y() < pt2->y() )
5450 return 0.0;
5451 else if ( pt1->y() > pt2->y() )
5452 return M_PI;
5453 else
5454 return 0;
5455 }
5456
5457 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5458 {
5459 if ( pt1->x() < pt2->x() )
5460 return M_PI_2;
5461 else if ( pt1->x() > pt2->x() )
5462 return M_PI + ( M_PI_2 );
5463 else
5464 return 0;
5465 }
5466
5467 if ( pt1->x() < pt2->x() )
5468 {
5469 if ( pt1->y() < pt2->y() )
5470 {
5471 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5472 }
5473 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5474 {
5475 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5476 + ( M_PI_2 );
5477 }
5478 }
5479
5480 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5481 {
5482 if ( pt1->y() > pt2->y() )
5483 {
5484 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5485 + M_PI;
5486 }
5487 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5488 {
5489 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5490 + ( M_PI + ( M_PI_2 ) );
5491 }
5492 }
5493}
5494
5495static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5496{
5497 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5498 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5499 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
5500 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5501
5502 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5503 {
5504 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5505 return QVariant();
5506 }
5507
5508 const QgsPointXY point1 = geom1.asPoint();
5509 const QgsPointXY point2 = geom2.asPoint();
5510 if ( point1.isEmpty() || point2.isEmpty() )
5511 {
5512 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5513 return QVariant();
5514 }
5515
5517 if ( context )
5518 {
5519 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5520
5521 if ( !sourceCrs.isValid() )
5522 {
5523 sourceCrs = context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>();
5524 }
5525
5526 if ( ellipsoid.isEmpty() )
5527 {
5528 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5529 }
5530 }
5531
5532 if ( !sourceCrs.isValid() )
5533 {
5534 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5535 return QVariant();
5536 }
5537
5538 QgsDistanceArea da;
5539 da.setSourceCrs( sourceCrs, tContext );
5540 if ( !da.setEllipsoid( ellipsoid ) )
5541 {
5542 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5543 return QVariant();
5544 }
5545
5546 try
5547 {
5548 const double bearing = da.bearing( point1, point2 );
5549 if ( std::isfinite( bearing ) )
5550 {
5551 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5552 }
5553 }
5554 catch ( QgsCsException &cse )
5555 {
5556 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5557 return QVariant();
5558 }
5559 return QVariant();
5560}
5561
5562static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5563{
5564 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5565
5567 {
5568 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5569 return QVariant();
5570 }
5571
5572 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5573 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5574 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5575
5576 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5577 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5578
5579 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5580}
5581
5582static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5583{
5584 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5585 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5586
5587 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5588 if ( !pt1 && fGeom1.isMultipart() )
5589 {
5590 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5591 {
5592 if ( collection->numGeometries() == 1 )
5593 {
5594 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5595 }
5596 }
5597 }
5598 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5599 if ( !pt2 && fGeom2.isMultipart() )
5600 {
5601 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5602 {
5603 if ( collection->numGeometries() == 1 )
5604 {
5605 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5606 }
5607 }
5608 }
5609
5610 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5611 !pt1 || !pt2 )
5612 {
5613 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5614 return QVariant();
5615 }
5616
5617 return pt1->inclination( *pt2 );
5618
5619}
5620
5621static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5622{
5623 if ( values.length() != 3 )
5624 return QVariant();
5625
5626 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5627 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5628 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5629
5630 QgsGeometry geom = fGeom.extrude( x, y );
5631
5632 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5633 return result;
5634}
5635
5636static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5637{
5638 if ( values.length() < 2 )
5639 return QVariant();
5640
5641 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5642
5643 if ( !fGeom.isMultipart() )
5644 return values.at( 0 );
5645
5646 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5647 QVariant cachedExpression;
5648 if ( ctx )
5649 cachedExpression = ctx->cachedValue( expString );
5650 QgsExpression expression;
5651
5652 if ( cachedExpression.isValid() )
5653 {
5654 expression = cachedExpression.value<QgsExpression>();
5655 }
5656 else
5657 expression = QgsExpression( expString );
5658
5659 bool asc = values.value( 2 ).toBool();
5660
5661 QgsExpressionContext *unconstedContext = nullptr;
5662 QgsFeature f;
5663 if ( ctx )
5664 {
5665 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5666 // so no reason to worry
5667 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5668 f = ctx->feature();
5669 }
5670 else
5671 {
5672 // If there's no context provided, create a fake one
5673 unconstedContext = new QgsExpressionContext();
5674 }
5675
5676 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5677 Q_ASSERT( collection ); // Should have failed the multipart check above
5678
5680 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5681 QgsExpressionSorter sorter( orderBy );
5682
5683 QList<QgsFeature> partFeatures;
5684 partFeatures.reserve( collection->partCount() );
5685 for ( int i = 0; i < collection->partCount(); ++i )
5686 {
5687 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5688 partFeatures << f;
5689 }
5690
5691 sorter.sortFeatures( partFeatures, unconstedContext );
5692
5693 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5694
5695 Q_ASSERT( orderedGeom );
5696
5697 while ( orderedGeom->partCount() )
5698 orderedGeom->removeGeometry( 0 );
5699
5700 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5701 {
5702 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5703 }
5704
5705 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5706
5707 if ( !ctx )
5708 delete unconstedContext;
5709
5710 return result;
5711}
5712
5713static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5714{
5715 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5716 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5717
5718 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5719
5720 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5721 return result;
5722}
5723
5724static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5725{
5726 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5727 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5728
5729 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5730
5731 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5732 return result;
5733}
5734
5735static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5736{
5737 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5738 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5739
5740 QgsGeometry geom = lineGeom.interpolate( distance );
5741
5742 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5743 return result;
5744}
5745
5746static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5747{
5748 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5749 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5750 const bool use3DDistance = values.at( 2 ).toBool();
5751
5752 double x, y, z, distance;
5753
5754 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5755 if ( !line )
5756 {
5757 return QVariant();
5758 }
5759
5760 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
5761 {
5762 QgsPoint point( x, y );
5763 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
5764 {
5765 point.addZValue( z );
5766 }
5767 return QVariant::fromValue( QgsGeometry( point.clone() ) );
5768 }
5769
5770 return QVariant();
5771}
5772
5773static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5774{
5775 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5776 if ( lineGeom.type() != Qgis::GeometryType::Line )
5777 {
5778 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5779 return QVariant();
5780 }
5781
5782 const QgsCurve *curve = nullptr;
5783 if ( !lineGeom.isMultipart() )
5784 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5785 else
5786 {
5787 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5788 {
5789 if ( collection->numGeometries() > 0 )
5790 {
5791 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5792 }
5793 }
5794 }
5795 if ( !curve )
5796 return QVariant();
5797
5798 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5799 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5800
5801 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5802 QgsGeometry result( std::move( substring ) );
5803 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5804}
5805
5806static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5807{
5808 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5809 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5810
5811 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5812}
5813
5814static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5815{
5816 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5817 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5818 if ( vertex < 0 )
5819 {
5820 //negative idx
5821 int count = geom.constGet()->nCoordinates();
5822 vertex = count + vertex;
5823 }
5824
5825 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5826}
5827
5828static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5829{
5830 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5831 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5832 if ( vertex < 0 )
5833 {
5834 //negative idx
5835 int count = geom.constGet()->nCoordinates();
5836 vertex = count + vertex;
5837 }
5838
5839 return geom.distanceToVertex( vertex );
5840}
5841
5842static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5843{
5844 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5845 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5846
5847 double distance = lineGeom.lineLocatePoint( pointGeom );
5848
5849 return distance >= 0 ? distance : QVariant();
5850}
5851
5852static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5853{
5854 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5855 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5856 const bool use3DDistance = values.at( 2 ).toBool();
5857
5858 double x, y, z, distance;
5859
5860 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5861 if ( !line )
5862 {
5863 return QVariant();
5864 }
5865
5866 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
5867 return found ? distance : QVariant();
5868}
5869
5870static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5871{
5872 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5873 {
5874 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5875 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5876 }
5877
5878 if ( values.length() >= 1 )
5879 {
5880 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5881 return QVariant( qlonglong( std::round( number ) ) );
5882 }
5883
5884 return QVariant();
5885}
5886
5887static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5888{
5889 Q_UNUSED( values )
5890 Q_UNUSED( parent )
5891 return M_PI;
5892}
5893
5894static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5895{
5896 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5897 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5898 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5899 if ( places < 0 )
5900 {
5901 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5902 return QVariant();
5903 }
5904
5905 const bool omitGroupSeparator = values.value( 3 ).toBool();
5906 const bool trimTrailingZeros = values.value( 4 ).toBool();
5907
5908 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5909 if ( !omitGroupSeparator )
5910 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5911 else
5912 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5913
5914 QString res = locale.toString( value, 'f', places );
5915
5916 if ( trimTrailingZeros )
5917 {
5918#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5919 const QChar decimal = locale.decimalPoint();
5920 const QChar zeroDigit = locale.zeroDigit();
5921#else
5922 const QChar decimal = locale.decimalPoint().at( 0 );
5923 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5924#endif
5925
5926 if ( res.contains( decimal ) )
5927 {
5928 int trimPoint = res.length() - 1;
5929
5930 while ( res.at( trimPoint ) == zeroDigit )
5931 trimPoint--;
5932
5933 if ( res.at( trimPoint ) == decimal )
5934 trimPoint--;
5935
5936 res.truncate( trimPoint + 1 );
5937 }
5938 }
5939
5940 return res;
5941}
5942
5943static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5944{
5945 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5946 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5947 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5948
5949 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
5950 if ( format.indexOf( "Z" ) > 0 )
5951 datetime = datetime.toUTC();
5952
5953 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5954 return locale.toString( datetime, format );
5955}
5956
5957static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5958{
5959 const QVariant variant = values.at( 0 );
5960 bool isQColor;
5961 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
5962 if ( !color.isValid() )
5963 return QVariant();
5964
5965 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5966 if ( color.spec() == QColor::Spec::Cmyk )
5967 {
5968 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
5969 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
5970 }
5971 else
5972 {
5973 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
5974 color.setRgbF( avg, avg, avg, alpha );
5975 }
5976
5977 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
5978}
5979
5980static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5981{
5982 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5983 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5984 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5985 if ( ratio > 1 )
5986 {
5987 ratio = 1;
5988 }
5989 else if ( ratio < 0 )
5990 {
5991 ratio = 0;
5992 }
5993
5994 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5995 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5996 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5997 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5998
5999 QColor newColor( red, green, blue, alpha );
6000
6001 return QgsSymbolLayerUtils::encodeColor( newColor );
6002}
6003
6004static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6005{
6006 const QVariant variant1 = values.at( 0 );
6007 const QVariant variant2 = values.at( 1 );
6008
6009 if ( variant1.userType() != variant2.userType() )
6010 {
6011 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6012 return QVariant();
6013 }
6014
6015 bool isQColor;
6016 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6017 if ( !color1.isValid() )
6018 return QVariant();
6019
6020 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6021 if ( !color2.isValid() )
6022 return QVariant();
6023
6024 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6025 {
6026 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6027 return QVariant();
6028 }
6029
6030 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6031
6032 // TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
6033 // NOLINTBEGIN(bugprone-narrowing-conversions)
6034
6035 QColor newColor;
6036 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6037 if ( color1.spec() == QColor::Spec::Cmyk )
6038 {
6039 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6040 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6041 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6042 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6043 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6044 }
6045 else
6046 {
6047 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6048 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6049 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6050 newColor = QColor::fromRgbF( red, green, blue, alpha );
6051 }
6052
6053 // NOLINTEND(bugprone-narrowing-conversions)
6054
6055 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6056}
6057
6058static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6059{
6060 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6061 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6062 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6063 QColor color = QColor( red, green, blue );
6064 if ( ! color.isValid() )
6065 {
6066 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6067 color = QColor( 0, 0, 0 );
6068 }
6069
6070 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6071}
6072
6073static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6074{
6075 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6076 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6077 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6078 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6079 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6080 if ( ! color.isValid() )
6081 {
6082 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6083 return QVariant();
6084 }
6085
6086 return color;
6087}
6088
6089static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6090{
6091 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6092 QVariant value = node->eval( parent, context );
6093 if ( parent->hasEvalError() )
6094 {
6095 parent->setEvalErrorString( QString() );
6096 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6098 value = node->eval( parent, context );
6100 }
6101 return value;
6102}
6103
6104static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6105{
6106 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6108 QVariant value = node->eval( parent, context );
6110 if ( value.toBool() )
6111 {
6112 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6114 value = node->eval( parent, context );
6116 }
6117 else
6118 {
6119 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6121 value = node->eval( parent, context );
6123 }
6124 return value;
6125}
6126
6127static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6128{
6129 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6130 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6131 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6132 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6133 QColor color = QColor( red, green, blue, alpha );
6134 if ( ! color.isValid() )
6135 {
6136 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6137 color = QColor( 0, 0, 0 );
6138 }
6139 return QgsSymbolLayerUtils::encodeColor( color );
6140}
6141
6142QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6143{
6144 QgsGradientColorRamp expRamp;
6145 const QgsColorRamp *ramp = nullptr;
6146 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6147 {
6148 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6149 ramp = &expRamp;
6150 }
6151 else
6152 {
6153 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6154 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6155 if ( ! ramp )
6157 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6158 return QVariant();
6159 }
6160 }
6161
6162 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6163 QColor color = ramp->color( value );
6164 return color;
6165}
6166
6167QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6168{
6169 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6170 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6171}
6172
6173static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6174{
6175 // Hue ranges from 0 - 360
6176 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6177 // Saturation ranges from 0 - 100
6178 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6179 // Lightness ranges from 0 - 100
6180 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6181
6182 QColor color = QColor::fromHslF( hue, saturation, lightness );
6183
6184 if ( ! color.isValid() )
6185 {
6186 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6187 color = QColor( 0, 0, 0 );
6188 }
6189
6190 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6191}
6192
6193static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6194{
6195 // Hue ranges from 0 - 360
6196 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6197 // Saturation ranges from 0 - 100
6198 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6199 // Lightness ranges from 0 - 100
6200 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6201 // Alpha ranges from 0 - 255
6202 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6203
6204 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6205 if ( ! color.isValid() )
6206 {
6207 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6208 color = QColor( 0, 0, 0 );
6209 }
6210 return QgsSymbolLayerUtils::encodeColor( color );
6211}
6212
6213static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6214{
6215 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6216 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6217 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6218 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6219
6220 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6221 if ( ! color.isValid() )
6222 {
6223 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6224 return QVariant();
6225 }
6226
6227 return color;
6228}
6229
6230static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6231{
6232 // Hue ranges from 0 - 360
6233 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6234 // Saturation ranges from 0 - 100
6235 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6236 // Value ranges from 0 - 100
6237 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6238
6239 QColor color = QColor::fromHsvF( hue, saturation, value );
6240
6241 if ( ! color.isValid() )
6242 {
6243 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6244 color = QColor( 0, 0, 0 );
6245 }
6246
6247 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6248}
6249
6250static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6251{
6252 // Hue ranges from 0 - 360
6253 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6254 // Saturation ranges from 0 - 100
6255 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6256 // Value ranges from 0 - 100
6257 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6258 // Alpha ranges from 0 - 255
6259 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6260
6261 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6262 if ( ! color.isValid() )
6263 {
6264 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6265 color = QColor( 0, 0, 0 );
6266 }
6267 return QgsSymbolLayerUtils::encodeColor( color );
6268}
6269
6270static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6271{
6272 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6273 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6274 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6275 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6276 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6277
6278 if ( ! color.isValid() )
6279 {
6280 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6281 return QVariant();
6282 }
6283
6284 return color;
6285}
6286
6287static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6288{
6289 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6290 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6291 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6292 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6293 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6294
6295 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6296 if ( ! color.isValid() )
6297 {
6298 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6299 return QVariant();
6300 }
6301
6302 return color;
6303}
6304
6305static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6306{
6307 // Cyan ranges from 0 - 100
6308 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6309 // Magenta ranges from 0 - 100
6310 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6311 // Yellow ranges from 0 - 100
6312 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6313 // Black ranges from 0 - 100
6314 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6315
6316 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6317
6318 if ( ! color.isValid() )
6319 {
6320 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6321 color = QColor( 0, 0, 0 );
6322 }
6323
6324 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6325}
6326
6327static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6328{
6329 // Cyan ranges from 0 - 100
6330 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6331 // Magenta ranges from 0 - 100
6332 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6333 // Yellow ranges from 0 - 100
6334 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6335 // Black ranges from 0 - 100
6336 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6337 // Alpha ranges from 0 - 255
6338 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6339
6340 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6341 if ( ! color.isValid() )
6342 {
6343 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6344 color = QColor( 0, 0, 0 );
6345 }
6346 return QgsSymbolLayerUtils::encodeColor( color );
6347}
6348
6349static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6350{
6351 const QVariant variant = values.at( 0 );
6352 bool isQColor;
6353 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6354 if ( !color.isValid() )
6355 return QVariant();
6356
6357 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6358 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6359 return color.red();
6360 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6361 return color.green();
6362 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6363 return color.blue();
6364 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6365 return color.alpha();
6366 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6367 return static_cast< double >( color.hsvHueF() * 360 );
6368 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6369 return static_cast< double >( color.hsvSaturationF() * 100 );
6370 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6371 return static_cast< double >( color.valueF() * 100 );
6372 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6373 return static_cast< double >( color.hslHueF() * 360 );
6374 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6375 return static_cast< double >( color.hslSaturationF() * 100 );
6376 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6377 return static_cast< double >( color.lightnessF() * 100 );
6378 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6379 return static_cast< double >( color.cyanF() * 100 );
6380 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6381 return static_cast< double >( color.magentaF() * 100 );
6382 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6383 return static_cast< double >( color.yellowF() * 100 );
6384 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6385 return static_cast< double >( color.blackF() * 100 );
6386
6387 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6388 return QVariant();
6389}
6390
6391static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6392{
6393 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6394 if ( map.empty() )
6395 {
6396 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6397 return QVariant();
6398 }
6399
6400 QList< QColor > colors;
6402 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6403 {
6404 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6405 if ( !colors.last().isValid() )
6406 {
6407 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6408 return QVariant();
6409 }
6410
6411 double step = it.key().toDouble();
6412 if ( it == map.constBegin() )
6413 {
6414 if ( step != 0.0 )
6415 stops << QgsGradientStop( step, colors.last() );
6416 }
6417 else if ( it == map.constEnd() )
6418 {
6419 if ( step != 1.0 )
6420 stops << QgsGradientStop( step, colors.last() );
6421 }
6422 else
6423 {
6424 stops << QgsGradientStop( step, colors.last() );
6425 }
6426 }
6427 bool discrete = values.at( 1 ).toBool();
6428
6429 if ( colors.empty() )
6430 return QVariant();
6431
6432 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6433}
6434
6435static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6436{
6437 const QVariant variant = values.at( 0 );
6438 bool isQColor;
6439 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6440 if ( !color.isValid() )
6441 return QVariant();
6442
6443 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6444 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6445 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6446 color.setRed( std::clamp( value, 0, 255 ) );
6447 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6448 color.setGreen( std::clamp( value, 0, 255 ) );
6449 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6450 color.setBlue( std::clamp( value, 0, 255 ) );
6451 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6452 color.setAlpha( std::clamp( value, 0, 255 ) );
6453 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6454 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6455 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6456 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6457 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6458 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6459 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6460 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6461 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6462 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6463 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6464 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6465 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6466 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6467 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6468 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6469 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6470 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6471 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6472 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6473 else
6474 {
6475 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6476 return QVariant();
6477 }
6478 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6479}
6480
6481static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6482{
6483 const QVariant variant = values.at( 0 );
6484 bool isQColor;
6485 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6486 if ( !color.isValid() )
6487 return QVariant();
6488
6489 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6490
6491 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6492}
6493
6494static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6495{
6496 const QVariant variant = values.at( 0 );
6497 bool isQColor;
6498 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6499 if ( !color.isValid() )
6500 return QVariant();
6501
6502 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6503
6504 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6505}
6506
6507static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6508{
6509 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6510 QgsGeometry geom = feat.geometry();
6511 if ( !geom.isNull() )
6512 return QVariant::fromValue( geom );
6513 return QVariant();
6514}
6515
6516static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6517{
6518 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6519 if ( !feat.isValid() )
6520 return QVariant();
6521 return feat.id();
6522}
6523
6524static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6525{
6526 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6527 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
6528 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6529
6530 if ( !sCrs.isValid() )
6531 return QVariant::fromValue( fGeom );
6532
6533 if ( !dCrs.isValid() )
6534 return QVariant::fromValue( fGeom );
6535
6537 if ( context )
6538 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6539 QgsCoordinateTransform t( sCrs, dCrs, tContext );
6540 try
6541 {
6543 return QVariant::fromValue( fGeom );
6544 }
6545 catch ( QgsCsException &cse )
6546 {
6547 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6548 return QVariant();
6549 }
6550 return QVariant();
6551}
6552
6553
6554static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6555{
6556 bool foundLayer = false;
6557 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6558
6559 //no layer found
6560 if ( !featureSource || !foundLayer )
6561 {
6562 return QVariant();
6563 }
6564
6565 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6566
6568 req.setFilterFid( fid );
6569 req.setTimeout( 10000 );
6570 req.setRequestMayBeNested( true );
6571 if ( context )
6572 req.setFeedback( context->feedback() );
6573 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6574
6575 QgsFeature fet;
6576 QVariant result;
6577 if ( fIt.nextFeature( fet ) )
6578 result = QVariant::fromValue( fet );
6579
6580 return result;
6581}
6582
6583static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6584{
6585 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6586 bool foundLayer = false;
6587 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6588
6589 //no layer found
6590 if ( !featureSource || !foundLayer )
6591 {
6592 return QVariant();
6593 }
6595 QString cacheValueKey;
6596 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6597 {
6598 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6599
6600 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6601 QString filterString;
6602 for ( ; i != attributeMap.constEnd(); ++i )
6603 {
6604 if ( !filterString.isEmpty() )
6605 {
6606 filterString.append( " AND " );
6607 }
6608 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6609 }
6610 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6611 if ( context && context->hasCachedValue( cacheValueKey ) )
6612 {
6613 return context->cachedValue( cacheValueKey );
6614 }
6615 req.setFilterExpression( filterString );
6616 }
6617 else
6618 {
6619 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6620 int attributeId = featureSource->fields().lookupField( attribute );
6621 if ( attributeId == -1 )
6622 {
6623 return QVariant();
6624 }
6625
6626 const QVariant &attVal = values.at( 2 );
6627
6628 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6629 if ( context && context->hasCachedValue( cacheValueKey ) )
6630 {
6631 return context->cachedValue( cacheValueKey );
6632 }
6633
6635 }
6636 req.setLimit( 1 );
6637 req.setTimeout( 10000 );
6638 req.setRequestMayBeNested( true );
6639 if ( context )
6640 req.setFeedback( context->feedback() );
6641 if ( !parent->needsGeometry() )
6642 {
6644 }
6645 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6646
6647 QgsFeature fet;
6648 QVariant res;
6649 if ( fIt.nextFeature( fet ) )
6650 {
6651 res = QVariant::fromValue( fet );
6652 }
6653
6654 if ( context )
6655 context->setCachedValue( cacheValueKey, res );
6656 return res;
6657}
6658
6659static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6660{
6661 QVariant result;
6662 QString fieldName;
6663
6664 if ( context )
6665 {
6666 if ( !values.isEmpty() )
6667 {
6668 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6669 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6670 fieldName = col->name();
6671 else if ( values.size() == 2 )
6672 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6673 }
6674
6675 QVariant value = values.at( 0 );
6676
6677 const QgsFields fields = context->fields();
6678 int fieldIndex = fields.lookupField( fieldName );
6679
6680 if ( fieldIndex == -1 )
6681 {
6682 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6683 }
6684 else
6685 {
6686 // TODO this function is NOT thread safe
6688 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6690
6691 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6692 if ( context->hasCachedValue( cacheValueKey ) )
6693 {
6694 return context->cachedValue( cacheValueKey );
6695 }
6696
6697 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6699
6700 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6701
6702 QVariant cache;
6703 if ( !context->hasCachedValue( cacheKey ) )
6704 {
6705 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6706 context->setCachedValue( cacheKey, cache );
6707 }
6708 else
6709 cache = context->cachedValue( cacheKey );
6710
6711 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6712
6713 context->setCachedValue( cacheValueKey, result );
6714 }
6715 }
6716 else
6717 {
6718 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6719 }
6720
6721 return result;
6722}
6723
6724static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6725{
6726 const QVariant data = values.at( 0 );
6727 const QMimeDatabase db;
6728 return db.mimeTypeForData( data.toByteArray() ).name();
6729}
6730
6731static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6732{
6733 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6734
6735 bool foundLayer = false;
6736 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6737 {
6738 if ( !layer )
6739 return QVariant();
6740
6741 // here, we always prefer the layer metadata values over the older server-specific published values
6742 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6743 return layer->name();
6744 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6745 return layer->id();
6746 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6747 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
6748 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6749 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
6750 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6751 {
6752 QStringList keywords;
6753 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6754 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6755 {
6756 keywords.append( it.value() );
6757 }
6758 if ( !keywords.isEmpty() )
6759 return keywords;
6760 return layer->serverProperties()->keywordList();
6761 }
6762 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6763 return layer->serverProperties()->dataUrl();
6764 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6765 {
6766 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
6767 }
6768 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6769 return layer->serverProperties()->attributionUrl();
6770 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6771 return layer->publicSource();
6772 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6773 return layer->minimumScale();
6774 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6775 return layer->maximumScale();
6776 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6777 return layer->isEditable();
6778 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6779 return layer->crs().authid();
6780 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6781 return layer->crs().toProj();
6782 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6783 return layer->crs().description();
6784 else if ( QString::compare( layerProperty, QStringLiteral( "crs_ellipsoid" ), Qt::CaseInsensitive ) == 0 )
6785 return layer->crs().ellipsoidAcronym();
6786 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6787 {
6788 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6789 QVariant result = QVariant::fromValue( extentGeom );
6790 return result;
6791 }
6792 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6793 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6794 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6795 {
6796 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6797 return decodedUri.value( QStringLiteral( "path" ) );
6798 }
6799 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6800 {
6801 switch ( layer->type() )
6802 {
6804 return QCoreApplication::translate( "expressions", "Vector" );
6806 return QCoreApplication::translate( "expressions", "Raster" );
6808 return QCoreApplication::translate( "expressions", "Mesh" );
6810 return QCoreApplication::translate( "expressions", "Vector Tile" );
6812 return QCoreApplication::translate( "expressions", "Plugin" );
6814 return QCoreApplication::translate( "expressions", "Annotation" );
6816 return QCoreApplication::translate( "expressions", "Point Cloud" );
6818 return QCoreApplication::translate( "expressions", "Group" );
6820 return QCoreApplication::translate( "expressions", "Tiled Scene" );
6821 }
6822 }
6823 else
6824 {
6825 //vector layer methods
6826 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6827 if ( vLayer )
6828 {
6829 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6830 return vLayer->storageType();
6831 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6833 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6834 return QVariant::fromValue( vLayer->featureCount() );
6835 }
6836 }
6837
6838 return QVariant();
6839 }, foundLayer );
6840
6841 if ( !foundLayer )
6842 return QVariant();
6843 else
6844 return res;
6845}
6846
6847static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6848{
6849 const QString uriPart = values.at( 1 ).toString();
6850
6851 bool foundLayer = false;
6852
6853 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
6854 {
6855 if ( !layer->dataProvider() )
6856 {
6857 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
6858 return QVariant();
6859 }
6860
6861 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
6862
6863 if ( !uriPart.isNull() )
6864 {
6865 return decodedUri.value( uriPart );
6866 }
6867 else
6868 {
6869 return decodedUri;
6870 }
6871 }, foundLayer );
6872
6873 if ( !foundLayer )
6874 {
6875 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
6876 return QVariant();
6877 }
6878 else
6879 {
6880 return res;
6881 }
6882}
6883
6884static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6885{
6886 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6887 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6888
6889 bool foundLayer = false;
6890 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
6891 {
6892 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6893 if ( !rl )
6894 return QVariant();
6895
6896 if ( band < 1 || band > rl->bandCount() )
6897 {
6898 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
6899 return QVariant();
6900 }
6901
6903
6904 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6906 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6908 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6910 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6912 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6914 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6916 else
6917 {
6918 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6919 return QVariant();
6920 }
6921
6922 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6923 switch ( stat )
6924 {
6926 return stats.mean;
6928 return stats.stdDev;
6930 return stats.minimumValue;
6932 return stats.maximumValue;
6934 return stats.range;
6936 return stats.sum;
6937 default:
6938 break;
6939 }
6940 return QVariant();
6941 }, foundLayer );
6942
6943 if ( !foundLayer )
6944 {
6945#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
6946 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
6947#endif
6948 return QVariant();
6949 }
6950 else
6951 {
6952 return res;
6953 }
6954}
6955
6956static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6957{
6958 return values;
6959}
6960
6961static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6962{
6963 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6964 bool ascending = values.value( 1 ).toBool();
6965 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6966 return list;
6967}
6968
6969static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6970{
6971 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6972}
6973
6974static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6975{
6976 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6977}
6978
6979static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6980{
6981 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6982}
6983
6984static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6985{
6986 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6987 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6988 int match = 0;
6989 for ( const auto &item : listB )
6990 {
6991 if ( listA.contains( item ) )
6992 match++;
6993 }
6994
6995 return QVariant( match == listB.count() );
6996}
6997
6998static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6999{
7000 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7001}
7002
7003static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7004{
7005 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7006 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7007 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
7008 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7009 return list.at( list.length() + pos );
7010 return QVariant();
7011}
7012
7013static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7014{
7015 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7016 return list.value( 0 );
7017}
7018
7019static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7020{
7021 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7022 return list.value( list.size() - 1 );
7023}
7024
7025static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7026{
7027 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7028 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7029}
7030
7031static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7032{
7033 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7034 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7035}
7036
7037static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7038{
7039 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7040 int i = 0;
7041 double total = 0.0;
7042 for ( const QVariant &item : list )
7043 {
7044 switch ( item.userType() )
7045 {
7046 case QMetaType::Int:
7047 case QMetaType::UInt:
7048 case QMetaType::LongLong:
7049 case QMetaType::ULongLong:
7050 case QMetaType::Float:
7051 case QMetaType::Double:
7052 total += item.toDouble();
7053 ++i;
7054 break;
7055 }
7056 }
7057 return i == 0 ? QVariant() : total / i;
7058}
7059
7060static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7061{
7062 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7063 QVariantList numbers;
7064 for ( const auto &item : list )
7065 {
7066 switch ( item.userType() )
7067 {
7068 case QMetaType::Int:
7069 case QMetaType::UInt:
7070 case QMetaType::LongLong:
7071 case QMetaType::ULongLong:
7072 case QMetaType::Float:
7073 case QMetaType::Double:
7074 numbers.append( item );
7075 break;
7076 }
7077 }
7078 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7079 const int count = numbers.count();
7080 if ( count == 0 )
7081 {
7082 return QVariant();
7083 }
7084 else if ( count % 2 )
7085 {
7086 return numbers.at( count / 2 );
7087 }
7088 else
7089 {
7090 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7091 }
7092}
7093
7094static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7095{
7096 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7097 int i = 0;
7098 double total = 0.0;
7099 for ( const QVariant &item : list )
7100 {
7101 switch ( item.userType() )
7102 {
7103 case QMetaType::Int:
7104 case QMetaType::UInt:
7105 case QMetaType::LongLong:
7106 case QMetaType::ULongLong:
7107 case QMetaType::Float:
7108 case QMetaType::Double:
7109 total += item.toDouble();
7110 ++i;
7111 break;
7112 }
7113 }
7114 return i == 0 ? QVariant() : total;
7115}
7116
7117static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7118{
7119 QVariant result = value;
7120 result.convert( static_cast<int>( type ) );
7121 return result;
7122}
7123
7124static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7125{
7126 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7127 QHash< QVariant, int > hash;
7128 for ( const auto &item : list )
7129 {
7130 ++hash[item];
7131 }
7132 const QList< int > occurrences = hash.values();
7133 if ( occurrences.empty() )
7134 return QVariantList();
7135
7136 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7137
7138 const QString option = values.at( 1 ).toString();
7139 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7140 {
7141 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7142 }
7143 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7144 {
7145 if ( hash.isEmpty() )
7146 return QVariant();
7147
7148 return QVariant( hash.key( maxValue ) );
7149 }
7150 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7151 {
7152 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7153 }
7154 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
7155 {
7156 if ( maxValue * 2 <= list.size() )
7157 return QVariant();
7158
7159 return QVariant( hash.key( maxValue ) );
7160 }
7161 else
7162 {
7163 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7164 return QVariant();
7165 }
7166}
7167
7168static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7169{
7170 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7171 QHash< QVariant, int > hash;
7172 for ( const auto &item : list )
7173 {
7174 ++hash[item];
7175 }
7176 const QList< int > occurrences = hash.values();
7177 if ( occurrences.empty() )
7178 return QVariantList();
7179
7180 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7181
7182 const QString option = values.at( 1 ).toString();
7183 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7184 {
7185 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7186 }
7187 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7188 {
7189 if ( hash.isEmpty() )
7190 return QVariant();
7191
7192 return QVariant( hash.key( minValue ) );
7193 }
7194 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7195 {
7196 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7197 }
7198 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
7199 {
7200 if ( hash.isEmpty() )
7201 return QVariant();
7202
7203 // Remove the majority, all others are minority
7204 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7205 if ( maxValue * 2 > list.size() )
7206 hash.remove( hash.key( maxValue ) );
7207
7208 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7209 }
7210 else
7211 {
7212 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7213 return QVariant();
7214 }
7215}
7216
7217static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7218{
7219 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7220 list.append( values.at( 1 ) );
7221 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7222}
7223
7224static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7225{
7226 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7227 list.prepend( values.at( 1 ) );
7228 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7229}
7230
7231static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7232{
7233 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7234 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7235 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7236}
7237
7238static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7239{
7240 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7241 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7242 if ( position < 0 )
7243 position = position + list.length();
7244 if ( position >= 0 && position < list.length() )
7245 list.removeAt( position );
7246 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7247}
7248
7249static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7250{
7251 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7252 return QVariant();
7253
7254 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7255
7256 const QVariant toRemove = values.at( 1 );
7257 if ( QgsVariantUtils::isNull( toRemove ) )
7258 {
7259 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7260 {
7261 return QgsVariantUtils::isNull( element );
7262 } ), list.end() );
7263 }
7264 else
7265 {
7266 list.removeAll( toRemove );
7267 }
7268 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7269}
7270
7271static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7272{
7273 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7274 {
7275 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7276
7277 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7278 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7279 {
7280 int index = list.indexOf( it.key() );
7281 while ( index >= 0 )
7282 {
7283 list.replace( index, it.value() );
7284 index = list.indexOf( it.key() );
7285 }
7286 }
7287
7288 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7289 }
7290 else if ( values.count() == 3 )
7291 {
7292 QVariantList before;
7293 QVariantList after;
7294 bool isSingleReplacement = false;
7295
7296 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7297 {
7298 before = QVariantList() << values.at( 1 );
7299 }
7300 else
7301 {
7302 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7303 }
7304
7305 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7306 {
7307 after = QVariantList() << values.at( 2 );
7308 isSingleReplacement = true;
7309 }
7310 else
7311 {
7312 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7313 }
7314
7315 if ( !isSingleReplacement && before.length() != after.length() )
7316 {
7317 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7318 return QVariant();
7319 }
7320
7321 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7322 for ( int i = 0; i < before.length(); i++ )
7323 {
7324 int index = list.indexOf( before.at( i ) );
7325 while ( index >= 0 )
7326 {
7327 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7328 index = list.indexOf( before.at( i ) );
7329 }
7330 }
7331
7332 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7333 }
7334 else
7335 {
7336 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7337 return QVariant();
7338 }
7339}
7340
7341static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7342{
7343 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7344 QVariantList list_new;
7345
7346 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7347 {
7348 while ( list.removeOne( cur ) )
7349 {
7350 list_new.append( cur );
7351 }
7352 }
7353
7354 list_new.append( list );
7355
7356 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7357}
7358
7359static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7360{
7361 QVariantList list;
7362 for ( const QVariant &cur : values )
7363 {
7364 list += QgsExpressionUtils::getListValue( cur, parent );
7365 }
7366 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7367}
7368
7369static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7370{
7371 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7372 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7373 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7374 int slice_length = 0;
7375 // negative positions means positions taken relative to the end of the array
7376 if ( start_pos < 0 )
7377 {
7378 start_pos = list.length() + start_pos;
7379 }
7380 if ( end_pos >= 0 )
7381 {
7382 slice_length = end_pos - start_pos + 1;
7383 }
7384 else
7385 {
7386 slice_length = list.length() + end_pos - start_pos + 1;
7387 }
7388 //avoid negative lengths in QList.mid function
7389 if ( slice_length < 0 )
7390 {
7391 slice_length = 0;
7392 }
7393 list = list.mid( start_pos, slice_length );
7394 return list;
7395}
7396
7397static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7398{
7399 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7400 std::reverse( list.begin(), list.end() );
7401 return list;
7402}
7403
7404static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7405{
7406 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7407 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7408 for ( const QVariant &cur : array2 )
7409 {
7410 if ( array1.contains( cur ) )
7411 return QVariant( true );
7412 }
7413 return QVariant( false );
7414}
7415
7416static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7417{
7418 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7419
7420 QVariantList distinct;
7421
7422 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7423 {
7424 if ( !distinct.contains( *it ) )
7425 {
7426 distinct += ( *it );
7427 }
7428 }
7429
7430 return distinct;
7431}
7432
7433static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7434{
7435 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7436 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7437 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7438
7439 QString str;
7440
7441 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7442 {
7443 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7444 if ( it != ( array.constEnd() - 1 ) )
7445 {
7446 str += delimiter;
7447 }
7448 }
7449
7450 return QVariant( str );
7451}
7452
7453static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7454{
7455 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7456 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7457 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7458
7459 QStringList list = str.split( delimiter );
7460 QVariantList array;
7461
7462 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7463 {
7464 array += ( !( *it ).isEmpty() ) ? *it : empty;
7465 }
7466
7467 return array;
7468}
7469
7470static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7471{
7472 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7473 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7474 if ( document.isNull() )
7475 return QVariant();
7476
7477 return document.toVariant();
7478}
7479
7480static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7481{
7482 Q_UNUSED( parent )
7483 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7484 return QString( document.toJson( QJsonDocument::Compact ) );
7485}
7486
7487static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7488{
7489 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7490 if ( str.isEmpty() )
7491 return QVariantMap();
7492 str = str.trimmed();
7493
7494 return QgsHstoreUtils::parse( str );
7495}
7496
7497static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7498{
7499 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7500 return QgsHstoreUtils::build( map );
7501}
7502
7503static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7504{
7505 QVariantMap result;
7506 for ( int i = 0; i + 1 < values.length(); i += 2 )
7507 {
7508 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7509 }
7510 return result;
7511}
7512
7513static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7514{
7515 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7516 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7517 QVariantMap resultMap;
7518
7519 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7520 {
7521 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7522 }
7523
7524 return resultMap;
7525}
7526
7527static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7528{
7529 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7530}
7531
7532static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7533{
7534 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7535}
7536
7537static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7538{
7539 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7540 map.remove( values.at( 1 ).toString() );
7541 return map;
7542}
7543
7544static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7545{
7546 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7547 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7548 return map;
7549}
7550
7551static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7552{
7553 QVariantMap result;
7554 for ( const QVariant &cur : values )
7555 {
7556 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7557 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7558 result.insert( it.key(), it.value() );
7559 }
7560 return result;
7561}
7562
7563static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7564{
7565 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7566}
7567
7568static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7569{
7570 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7571}
7572
7573static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7574{
7575 const QString envVarName = values.at( 0 ).toString();
7576 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7577 return QVariant();
7578
7579 return QProcessEnvironment::systemEnvironment().value( envVarName );
7580}
7581
7582static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7583{
7584 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7585 if ( parent->hasEvalError() )
7586 {
7587 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7588 return QVariant();
7589 }
7590 return QFileInfo( file ).completeBaseName();
7591}
7592
7593static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7594{
7595 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7596 if ( parent->hasEvalError() )
7597 {
7598 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7599 return QVariant();
7600 }
7601 return QFileInfo( file ).completeSuffix();
7602}
7603
7604static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7605{
7606 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7607 if ( parent->hasEvalError() )
7608 {
7609 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7610 return QVariant();
7611 }
7612 return QFileInfo::exists( file );
7613}
7614
7615static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7616{
7617 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7618 if ( parent->hasEvalError() )
7619 {
7620 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7621 return QVariant();
7622 }
7623 return QFileInfo( file ).fileName();
7624}
7625
7626static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7627{
7628 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7629 if ( parent->hasEvalError() )
7630 {
7631 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7632 return QVariant();
7633 }
7634 return QFileInfo( file ).isFile();
7635}
7636
7637static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7638{
7639 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7640 if ( parent->hasEvalError() )
7641 {
7642 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7643 return QVariant();
7644 }
7645 return QFileInfo( file ).isDir();
7646}
7647
7648static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7649{
7650 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7651 if ( parent->hasEvalError() )
7652 {
7653 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7654 return QVariant();
7655 }
7656 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7657}
7658
7659static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7660{
7661 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7662 if ( parent->hasEvalError() )
7663 {
7664 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7665 return QVariant();
7666 }
7667 return QFileInfo( file ).size();
7668}
7669
7670static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7671{
7672 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7673}
7674
7675static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7676{
7677 QVariant hash;
7678 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7679 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7680
7681 if ( method == QLatin1String( "md4" ) )
7682 {
7683 hash = fcnHash( str, QCryptographicHash::Md4 );
7684 }
7685 else if ( method == QLatin1String( "md5" ) )
7686 {
7687 hash = fcnHash( str, QCryptographicHash::Md5 );
7688 }
7689 else if ( method == QLatin1String( "sha1" ) )
7690 {
7691 hash = fcnHash( str, QCryptographicHash::Sha1 );
7692 }
7693 else if ( method == QLatin1String( "sha224" ) )
7694 {
7695 hash = fcnHash( str, QCryptographicHash::Sha224 );
7696 }
7697 else if ( method == QLatin1String( "sha256" ) )
7698 {
7699 hash = fcnHash( str, QCryptographicHash::Sha256 );
7700 }
7701 else if ( method == QLatin1String( "sha384" ) )
7702 {
7703 hash = fcnHash( str, QCryptographicHash::Sha384 );
7704 }
7705 else if ( method == QLatin1String( "sha512" ) )
7706 {
7707 hash = fcnHash( str, QCryptographicHash::Sha512 );
7708 }
7709 else if ( method == QLatin1String( "sha3_224" ) )
7710 {
7711 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7712 }
7713 else if ( method == QLatin1String( "sha3_256" ) )
7714 {
7715 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7716 }
7717 else if ( method == QLatin1String( "sha3_384" ) )
7718 {
7719 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7720 }
7721 else if ( method == QLatin1String( "sha3_512" ) )
7722 {
7723 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7724 }
7725 else if ( method == QLatin1String( "keccak_224" ) )
7726 {
7727 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7728 }
7729 else if ( method == QLatin1String( "keccak_256" ) )
7730 {
7731 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7732 }
7733 else if ( method == QLatin1String( "keccak_384" ) )
7734 {
7735 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7736 }
7737 else if ( method == QLatin1String( "keccak_512" ) )
7738 {
7739 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7740 }
7741 else
7742 {
7743 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7744 }
7745 return hash;
7746}
7747
7748static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7749{
7750 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7751}
7752
7753static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7754{
7755 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7756}
7757
7758static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7759{
7760 const QByteArray input = values.at( 0 ).toByteArray();
7761 return QVariant( QString( input.toBase64() ) );
7762}
7763
7764static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7765{
7766 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7767 QUrlQuery query;
7768 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7769 {
7770 query.addQueryItem( it.key(), it.value().toString() );
7771 }
7772 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7773}
7774
7775static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7776{
7777 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7778 const QByteArray base64 = value.toLocal8Bit();
7779 const QByteArray decoded = QByteArray::fromBase64( base64 );
7780 return QVariant( decoded );
7781}
7782
7783typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7784
7785static 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 )
7786{
7787
7788 if ( ! context )
7789 {
7790 parent->setEvalErrorString( QStringLiteral( "This function was called without an expression context." ) );
7791 return QVariant();
7792 }
7793
7794 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7795 // TODO this function is NOT thread safe
7797 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7799
7800 QgsFeatureRequest request;
7801 request.setTimeout( 10000 );
7802 request.setRequestMayBeNested( true );
7803 request.setFeedback( context->feedback() );
7804
7805 // First parameter is the overlay layer
7806 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7808
7809 const bool layerCanBeCached = node->isStatic( parent, context );
7810 QVariant targetLayerValue = node->eval( parent, context );
7812
7813 // Second parameter is the expression to evaluate (or null for testonly)
7814 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7816 QString subExpString = node->dump();
7817
7818 bool testOnly = ( subExpString == "NULL" );
7819 // TODO this function is NOT thread safe
7821 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7823 if ( !targetLayer ) // No layer, no joy
7824 {
7825 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7826 return QVariant();
7827 }
7828
7829 // Third parameter is the filtering expression
7830 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7832 QString filterString = node->dump();
7833 if ( filterString != "NULL" )
7834 {
7835 request.setFilterExpression( filterString ); //filter cached features
7836 }
7837
7838 // Fourth parameter is the limit
7839 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7841 QVariant limitValue = node->eval( parent, context );
7843 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7844
7845 // Fifth parameter (for nearest only) is the max distance
7846 double max_distance = 0;
7847 if ( isNearestFunc ) //maxdistance param handling
7848 {
7849 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7851 QVariant distanceValue = node->eval( parent, context );
7853 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
7854 }
7855
7856 // Fifth or sixth (for nearest only) parameter is the cache toggle
7857 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
7859 QVariant cacheValue = node->eval( parent, context );
7861 bool cacheEnabled = cacheValue.toBool();
7862
7863 // Sixth parameter (for intersects only) is the min overlap (area or length)
7864 // Seventh parameter (for intersects only) is the min inscribed circle radius
7865 // Eighth parameter (for intersects only) is the return_details
7866 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
7867 double minOverlap { -1 };
7868 double minInscribedCircleRadius { -1 };
7869 bool returnDetails = false; //#spellok
7870 bool sortByMeasure = false;
7871 bool sortAscending = false;
7872 bool requireMeasures = false;
7873 bool overlapOrRadiusFilter = false;
7874 if ( isIntersectsFunc )
7875 {
7876
7877 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7879 const QVariant minOverlapValue = node->eval( parent, context );
7881 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
7882 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7884 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
7886 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
7887 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
7888 // Return measures is only effective when an expression is set
7889 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
7890 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
7891 // Sort by measures is only effective when an expression is set
7892 const QString sorting { node->eval( parent, context ).toString().toLower() };
7893 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
7894 sortAscending = sorting.startsWith( "asc" );
7895 requireMeasures = sortByMeasure || returnDetails; //#spellok
7896 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
7897 }
7898
7899
7900 FEAT_FROM_CONTEXT( context, feat )
7901 const QgsGeometry geometry = feat.geometry();
7902
7903 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7904 {
7905 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7906 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7907 }
7908
7909 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7910
7911 QgsRectangle intDomain = geometry.boundingBox();
7912 if ( bboxGrow != 0 )
7913 {
7914 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7915 }
7916
7917 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7918
7919 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7920 // Otherwise, it can be toggled by the user
7921 QgsSpatialIndex spatialIndex;
7922 QgsVectorLayer *cachedTarget;
7923 QList<QgsFeature> features;
7924 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7925 {
7926 // If the cache (local spatial index) is enabled, we materialize the whole
7927 // layer, then do the request on that layer instead.
7928 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7929 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7930
7931 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7932 {
7933 cachedTarget = targetLayer->materialize( request );
7934 if ( layerCanBeCached )
7935 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7936 }
7937 else
7938 {
7939 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7940 }
7941
7942 if ( !context->hasCachedValue( cacheIndex ) )
7943 {
7944 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7945 if ( layerCanBeCached )
7946 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7947 }
7948 else
7949 {
7950 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7951 }
7952
7953 QList<QgsFeatureId> fidsList;
7954 if ( isNearestFunc )
7955 {
7956 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7957 }
7958 else
7959 {
7960 fidsList = spatialIndex.intersects( intDomain );
7961 }
7962
7963 QListIterator<QgsFeatureId> i( fidsList );
7964 while ( i.hasNext() )
7965 {
7966 QgsFeatureId fId2 = i.next();
7967 if ( sameLayers && feat.id() == fId2 )
7968 continue;
7969 features.append( cachedTarget->getFeature( fId2 ) );
7970 }
7971
7972 }
7973 else
7974 {
7975 // If the cache (local spatial index) is not enabled, we directly
7976 // get the features from the target layer
7977 request.setFilterRect( intDomain );
7978 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7979 QgsFeature feat2;
7980 while ( fit.nextFeature( feat2 ) )
7981 {
7982 if ( sameLayers && feat.id() == feat2.id() )
7983 continue;
7984 features.append( feat2 );
7985 }
7986 }
7987
7988 QgsExpression subExpression;
7989 QgsExpressionContext subContext;
7990 if ( !testOnly )
7991 {
7992 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7993 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7994
7995 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7996 {
7997 subExpression = QgsExpression( subExpString );
7999 subExpression.prepare( &subContext );
8000 }
8001 else
8002 {
8003 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8004 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8005 }
8006 }
8007
8008 // //////////////////////////////////////////////////////////////////
8009 // Helper functions for geometry tests
8010
8011 // Test function for linestring geometries, returns TRUE if test passes
8012 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
8013 {
8014 bool testResult { false };
8015 // For return measures:
8016 QVector<double> overlapValues;
8017 const QgsGeometry merged { intersection.mergeLines() };
8018 for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
8019 {
8020 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
8021 // Check min overlap for intersection (if set)
8022 if ( minOverlap != -1 || requireMeasures )
8023 {
8024 overlapValue = geom->length();
8025 overlapValues.append( overlapValue );
8026 if ( minOverlap != -1 )
8027 {
8028 if ( overlapValue >= minOverlap )
8029 {
8030 testResult = true;
8031 }
8032 else
8033 {
8034 continue;
8035 }
8036 }
8037 }
8038 }
8039
8040 if ( ! overlapValues.isEmpty() )
8041 {
8042 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8043 }
8044
8045 return testResult;
8046 };
8047
8048 // Test function for polygon geometries, returns TRUE if test passes
8049 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
8050 {
8051 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8052 bool testResult { false };
8053 // For return measures:
8054 QVector<double> overlapValues;
8055 QVector<double> radiusValues;
8056 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8057 {
8058 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
8059 // Check min overlap for intersection (if set)
8060 if ( minOverlap != -1 || requireMeasures )
8061 {
8062 overlapValue = geom->area();
8063 overlapValues.append( geom->area() );
8064 if ( minOverlap != - 1 )
8065 {
8066 if ( overlapValue >= minOverlap )
8067 {
8068 testResult = true;
8069 }
8070 else
8071 {
8072 continue;
8073 }
8074 }
8075 }
8076
8077 // Check min inscribed circle radius for intersection (if set)
8078 if ( minInscribedCircleRadius != -1 || requireMeasures )
8079 {
8080 const QgsRectangle bbox = geom->boundingBox();
8081 const double width = bbox.width();
8082 const double height = bbox.height();
8083 const double size = width > height ? width : height;
8084 const double tolerance = size / 100.0;
8085 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8086 testResult = radiusValue >= minInscribedCircleRadius;
8087 radiusValues.append( radiusValues );
8088 }
8089 } // end for parts
8090
8091 // Get the max values
8092 if ( !radiusValues.isEmpty() )
8093 {
8094 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8095 }
8096
8097 if ( ! overlapValues.isEmpty() )
8098 {
8099 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8100 }
8101
8102 return testResult;
8103
8104 };
8105
8106
8107 bool found = false;
8108 int foundCount = 0;
8109 QVariantList results;
8110
8111 QListIterator<QgsFeature> i( features );
8112 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8113 {
8114
8115 QgsFeature feat2 = i.next();
8116
8117
8118 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8119 {
8120
8121 double overlapValue = -1;
8122 double radiusValue = -1;
8123
8124 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8125 {
8126
8127 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8128
8129 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8130 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8131 {
8132 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8133 intersection = QgsGeometry();
8134 QgsMultiPolygonXY poly;
8135 QgsMultiPolylineXY line;
8136 QgsMultiPointXY point;
8137 for ( const auto &geom : std::as_const( geometries ) )
8138 {
8139 switch ( geom.type() )
8140 {
8142 {
8143 poly.append( geom.asPolygon() );
8144 break;
8145 }
8147 {
8148 line.append( geom.asPolyline() );
8149 break;
8150 }
8152 {
8153 point.append( geom.asPoint() );
8154 break;
8155 }
8158 {
8159 break;
8160 }
8161 }
8162 }
8163
8164 switch ( geometry.type() )
8165 {
8167 {
8168 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8169 break;
8170 }
8172 {
8173 intersection = QgsGeometry::fromMultiPolylineXY( line );
8174 break;
8175 }
8177 {
8178 intersection = QgsGeometry::fromMultiPointXY( point );
8179 break;
8180 }
8183 {
8184 break;
8185 }
8186 }
8187 }
8188
8189 // Depending on the intersection geometry type and on the geometry type of
8190 // the tested geometry we can run different tests and collect different measures
8191 // that can be used for sorting (if required).
8192 switch ( intersection.type() )
8193 {
8194
8196 {
8197
8198 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8199 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8200
8201 if ( ! testResult && overlapOrRadiusFilter )
8202 {
8203 continue;
8204 }
8205
8206 break;
8207 }
8208
8210 {
8211
8212 // If the intersection is a linestring and a minimum circle is required
8213 // we can discard this result immediately.
8214 if ( minInscribedCircleRadius != -1 )
8215 {
8216 continue;
8217 }
8218
8219 // Otherwise a test for the overlap value is performed.
8220 const bool testResult { testLinestring( intersection, overlapValue ) };
8221
8222 if ( ! testResult && overlapOrRadiusFilter )
8223 {
8224 continue;
8225 }
8226
8227 break;
8228 }
8229
8231 {
8232
8233 // If the intersection is a point and a minimum circle is required
8234 // we can discard this result immediately.
8235 if ( minInscribedCircleRadius != -1 )
8236 {
8237 continue;
8238 }
8239
8240 bool testResult { false };
8241 if ( minOverlap != -1 || requireMeasures )
8242 {
8243 // Initially set this to 0 because it's a point intersection...
8244 overlapValue = 0;
8245 // ... but if the target geometry is not a point and the source
8246 // geometry is a point, we must record the length or the area
8247 // of the intersected geometry and use that as a measure for
8248 // sorting or reporting.
8249 if ( geometry.type() == Qgis::GeometryType::Point )
8250 {
8251 switch ( feat2.geometry().type() )
8252 {
8256 {
8257 break;
8258 }
8260 {
8261 testResult = testLinestring( feat2.geometry(), overlapValue );
8262 break;
8263 }
8265 {
8266 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8267 break;
8268 }
8269 }
8270 }
8271
8272 if ( ! testResult && overlapOrRadiusFilter )
8273 {
8274 continue;
8275 }
8276
8277 }
8278 break;
8279 }
8280
8283 {
8284 continue;
8285 }
8286 }
8287 }
8288
8289 found = true;
8290 foundCount++;
8291
8292 // We just want a single boolean result if there is any intersect: finish and return true
8293 if ( testOnly )
8294 break;
8295
8296 if ( !invert )
8297 {
8298 // We want a list of attributes / geometries / other expression values, evaluate now
8299 subContext.setFeature( feat2 );
8300 const QVariant expResult = subExpression.evaluate( &subContext );
8301
8302 if ( requireMeasures )
8303 {
8304 QVariantMap resultRecord;
8305 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
8306 resultRecord.insert( QStringLiteral( "result" ), expResult );
8307 // Overlap is always added because return measures was set
8308 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
8309 // Radius is only added when is different than -1 (because for linestrings is not set)
8310 if ( radiusValue != -1 )
8311 {
8312 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
8313 }
8314 results.append( resultRecord );
8315 }
8316 else
8317 {
8318 results.append( expResult );
8319 }
8320 }
8321 else
8322 {
8323 // If not, results is a list of found ids, which we'll inverse and evaluate below
8324 results.append( feat2.id() );
8325 }
8326 }
8327 }
8328
8329 if ( testOnly )
8330 {
8331 if ( invert )
8332 found = !found;//for disjoint condition
8333 return found;
8334 }
8335
8336 if ( !invert )
8337 {
8338 if ( requireMeasures )
8339 {
8340 if ( sortByMeasure )
8341 {
8342 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8343 {
8344 return sortAscending ?
8345 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
8346 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
8347 } );
8348 }
8349 // Resize
8350 if ( limit > 0 && results.size() > limit )
8351 {
8352 results.erase( results.begin() + limit );
8353 }
8354
8355 if ( ! returnDetails ) //#spellok
8356 {
8357 QVariantList expResults;
8358 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8359 {
8360 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
8361 }
8362 return expResults;
8363 }
8364 }
8365
8366 return results;
8367 }
8368
8369 // for disjoint condition returns the results for cached layers not intersected feats
8370 QVariantList disjoint_results;
8371 QgsFeature feat2;
8372 QgsFeatureRequest request2;
8373 request2.setLimit( limit );
8374 if ( context )
8375 request2.setFeedback( context->feedback() );
8376 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8377 while ( fi.nextFeature( feat2 ) )
8378 {
8379 if ( !results.contains( feat2.id() ) )
8380 {
8381 subContext.setFeature( feat2 );
8382 disjoint_results.append( subExpression.evaluate( &subContext ) );
8383 }
8384 }
8385 return disjoint_results;
8386
8387}
8388
8389// Intersect functions:
8390
8391static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8392{
8393 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8394}
8395
8396static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8397{
8398 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8399}
8400
8401static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8402{
8403 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8404}
8405
8406static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8407{
8408 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8409}
8410
8411static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8412{
8413 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8414}
8415
8416static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8417{
8418 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8419}
8421static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8422{
8423 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8424}
8425
8426static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8427{
8428 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8429}
8430
8431const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8432{
8433 // The construction of the list isn't thread-safe, and without the mutex,
8434 // crashes in the WFS provider may occur, since it can parse expressions
8435 // in parallel.
8436 // The mutex needs to be recursive.
8437 QMutexLocker locker( &sFunctionsMutex );
8438
8439 QList<QgsExpressionFunction *> &functions = *sFunctions();
8440
8441 if ( functions.isEmpty() )
8442 {
8444 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8445 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
8446 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
8447
8448 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8449 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8450 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8451
8452 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8453 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8454
8455 functions
8456 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
8457 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
8458 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
8459 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
8460 << 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" ) )
8461 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
8462 << 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" ) )
8463 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
8464 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
8465 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
8466 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
8467 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
8468 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
8469 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
8470 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
8471 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
8472 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
8473 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
8474 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
8475 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
8476
8477 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
8478 randFunc->setIsStatic( false );
8479 functions << randFunc;
8480
8481 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" ) );
8482 randfFunc->setIsStatic( false );
8483 functions << randfFunc;
8484
8485 functions
8486 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8487 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8488 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
8489 << 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" ) )
8490 << 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" ) )
8491 << 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" ) )
8492 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8493 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8494 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8495 << new QgsStaticExpressionFunction( QStringLiteral( "to_bool" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToBool, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tobool" ), /* handlesNull = */ true )
8496 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8497 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8498 << 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" ) )
8499 << 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" ) )
8500 << 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" ) )
8501 << 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" ) )
8502 << 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" ) )
8503 << 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" ) )
8504 << 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" ) )
8505 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8506 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8507 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8508 << 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 )
8509 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8510
8511 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8513 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8514 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8515 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8516 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8517 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8518 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8519 fcnAggregate,
8520 QStringLiteral( "Aggregates" ),
8521 QString(),
8522 []( const QgsExpressionNodeFunction * node )
8523 {
8524 // usesGeometry callback: return true if @parent variable is referenced
8525
8526 if ( !node )
8527 return true;
8528
8529 if ( !node->args() )
8530 return false;
8531
8532 QSet<QString> referencedVars;
8533 if ( node->args()->count() > 2 )
8534 {
8535 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8536 referencedVars = subExpressionNode->referencedVariables();
8537 }
8538
8539 if ( node->args()->count() > 3 )
8540 {
8541 QgsExpressionNode *filterNode = node->args()->at( 3 );
8542 referencedVars.unite( filterNode->referencedVariables() );
8543 }
8544 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8545 },
8546 []( const QgsExpressionNodeFunction * node )
8547 {
8548 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8549
8550 if ( !node )
8551 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8552
8553 if ( !node->args() )
8554 return QSet<QString>();
8555
8556 QSet<QString> referencedCols;
8557 QSet<QString> referencedVars;
8558
8559 if ( node->args()->count() > 2 )
8560 {
8561 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8562 referencedVars = subExpressionNode->referencedVariables();
8563 referencedCols = subExpressionNode->referencedColumns();
8564 }
8565 if ( node->args()->count() > 3 )
8566 {
8567 QgsExpressionNode *filterNode = node->args()->at( 3 );
8568 referencedVars = filterNode->referencedVariables();
8569 referencedCols.unite( filterNode->referencedColumns() );
8570 }
8571
8572 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8573 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8574 else
8575 return referencedCols;
8576 },
8577 true
8578 )
8579
8580 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8581 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8582 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8583 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8584 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8585 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8586 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8587
8588 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8589 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8590 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8591 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8592 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8593 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8594 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8595 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8596 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8597 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8598 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8599 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8600 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8601 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8602 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8603 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8604 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8605 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8606 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8607 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8608 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8609
8610 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8611 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8612
8613 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8614 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8615 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8616 fcnAge, QStringLiteral( "Date and Time" ) )
8617 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8618 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8619 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8620 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8621 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8622 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8623 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8624 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8625 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8626 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8627 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8628 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8629 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8630 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8631 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8632 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8633 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8634 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8635 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8636 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8637 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8638 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8639 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8640 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8641 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8642 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8643 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8644 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8645 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8646 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8647 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8648 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8649 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8650 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8651 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8652 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8653 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8654 << new QgsStaticExpressionFunction( QStringLiteral( "ltrim" ), QgsExpressionFunction::ParameterList()
8655 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8656 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnLTrim, QStringLiteral( "String" ) )
8657 << new QgsStaticExpressionFunction( QStringLiteral( "rtrim" ), QgsExpressionFunction::ParameterList()
8658 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8659 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnRTrim, QStringLiteral( "String" ) )
8660 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8661 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8662 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8663 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8664 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8665 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8666 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8667 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8668 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8669 << new QgsStaticExpressionFunction( QStringLiteral( "repeat" ), { QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )}, fcnRepeat, QStringLiteral( "String" ) )
8670 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8671 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8672 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8673 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8674 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8675 false, QSet< QString >(), false, QStringList(), true )
8676 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8677 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8678 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8679 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8680 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8681 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8682 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8683 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8684 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8685 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8686 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8687 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8688 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8689 << 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" ) )
8690 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8691 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8692 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8693 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8694 fcnColorMixRgb, QStringLiteral( "Color" ) )
8695 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8696 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8697 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8698 fcnColorMix, QStringLiteral( "Color" ) )
8699 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8700 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8701 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8702 fcnColorRgb, QStringLiteral( "Color" ) )
8703 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgbf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8704 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8705 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8706 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8707 fcnColorRgbF, QStringLiteral( "Color" ) )
8708 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8709 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8710 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8711 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8712 fncColorRgba, QStringLiteral( "Color" ) )
8713 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8714 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8715 fcnRampColor, QStringLiteral( "Color" ) )
8716 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color_object" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8717 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8718 fcnRampColorObject, QStringLiteral( "Color" ) )
8719 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8720 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8721 fcnCreateRamp, QStringLiteral( "Color" ) )
8722 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8723 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8724 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8725 fcnColorHsl, QStringLiteral( "Color" ) )
8726 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8727 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8728 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8729 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8730 fncColorHsla, QStringLiteral( "Color" ) )
8731 << new QgsStaticExpressionFunction( QStringLiteral( "color_hslf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8732 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8733 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8734 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8735 fcnColorHslF, QStringLiteral( "Color" ) )
8736 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8737 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8738 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8739 fcnColorHsv, QStringLiteral( "Color" ) )
8740 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8741 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8742 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8743 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8744 fncColorHsva, QStringLiteral( "Color" ) )
8745 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsvf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8746 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8747 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8748 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8749 fcnColorHsvF, QStringLiteral( "Color" ) )
8750 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8751 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8752 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8753 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8754 fcnColorCmyk, QStringLiteral( "Color" ) )
8755 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8756 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8757 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8758 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8759 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8760 fncColorCmyka, QStringLiteral( "Color" ) )
8761 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmykf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8762 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8763 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8764 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8765 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8766 fcnColorCmykF, QStringLiteral( "Color" ) )
8767 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8768 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8769 fncColorPart, QStringLiteral( "Color" ) )
8770 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8771 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8772 fncDarker, QStringLiteral( "Color" ) )
8773 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8774 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8775 fncLighter, QStringLiteral( "Color" ) )
8776 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8777
8778 // file info
8779 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8780 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8781 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8782 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8783 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8784 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8785 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8786 fcnFileName, QStringLiteral( "Files and Paths" ) )
8787 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8788 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8789 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8790 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8791 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8792 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8793 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8794 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8795
8796 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8797 fcnExif, QStringLiteral( "Files and Paths" ) )
8798 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8799 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8800
8801 // hash
8802 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8803 fcnGenericHash, QStringLiteral( "Conversions" ) )
8804 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8805 fcnHashMd5, QStringLiteral( "Conversions" ) )
8806 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8807 fcnHashSha256, QStringLiteral( "Conversions" ) )
8808
8809 //base64
8810 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8811 fcnToBase64, QStringLiteral( "Conversions" ) )
8812 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8813 fcnFromBase64, QStringLiteral( "Conversions" ) )
8814
8815 // deprecated stuff - hidden from users
8816 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8817
8818 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8819 geomFunc->setIsStatic( false );
8820 functions << geomFunc;
8821
8822 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8823 areaFunc->setIsStatic( false );
8824 functions << areaFunc;
8825
8826 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8827
8828 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8829 lengthFunc->setIsStatic( false );
8830 functions << lengthFunc;
8831
8832 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8833 perimeterFunc->setIsStatic( false );
8834 functions << perimeterFunc;
8835
8836 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8837
8838 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8840 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
8841
8842 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
8843 xFunc->setIsStatic( false );
8844 functions << xFunc;
8845
8846 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
8847 yFunc->setIsStatic( false );
8848 functions << yFunc;
8849
8850 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
8851 zFunc->setIsStatic( false );
8852 functions << zFunc;
8853
8854 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
8855 {
8856 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
8857 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
8858 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
8859 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
8860 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
8861 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
8862 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
8863 };
8864 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
8865 while ( i.hasNext() )
8866 {
8867 i.next();
8869 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8870 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8871 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8872 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
8873 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
8874 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
8875 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
8876 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
8877 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
8878 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8879
8880 // The current feature is accessed for the geometry, so this should not be cached
8881 fcnGeomOverlayFunc->setIsStatic( false );
8882 functions << fcnGeomOverlayFunc;
8883 }
8884
8885 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
8886 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8887 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8888 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8889 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
8890 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
8891 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
8892 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8893 // The current feature is accessed for the geometry, so this should not be cached
8894 fcnGeomOverlayNearestFunc->setIsStatic( false );
8895 functions << fcnGeomOverlayNearestFunc;
8896
8897 functions
8898 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
8899 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
8900 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
8901 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
8902 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
8903 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
8904 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
8905 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
8906 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8907 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
8908 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
8909 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
8910 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
8911 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
8912 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8913 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
8914 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
8915 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
8916 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
8917 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
8918 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8919 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8920 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
8921 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
8922 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
8923 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8924 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8925 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8926 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
8927 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
8928 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8929 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
8930 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
8931 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8932 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8933 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
8934 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
8935 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8936 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8937 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
8938 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
8939 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
8940 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
8941 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8942 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
8943 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
8944 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
8945 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8946 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8947 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
8948 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
8949 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
8950 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
8951 {
8952 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8953#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
8954 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
8955#else
8956 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
8957#endif
8958 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
8959 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
8960
8961 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
8962 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
8963 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
8964 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
8965
8966 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
8967 xAtFunc->setIsStatic( false );
8968 functions << xAtFunc;
8969
8970
8971 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
8972 yAtFunc->setIsStatic( false );
8973 functions << yAtFunc;
8974
8975 functions
8976 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
8977 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
8978 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
8979 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
8980 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
8981 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
8982 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8983 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
8984 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
8985 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
8986 << 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" ) )
8987 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8988 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8989 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
8990 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8991 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8992 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
8993 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8994 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8995 fcnTouches, QStringLiteral( "GeometryGroup" ) )
8996 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8997 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8998 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
8999 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9000 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9001 fcnContains, QStringLiteral( "GeometryGroup" ) )
9002 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9003 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9004 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
9005 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9006 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9007 fcnWithin, QStringLiteral( "GeometryGroup" ) )
9008 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9009 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
9010 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
9011 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
9012 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9013 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
9014 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
9015 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
9016 fcnRotate, QStringLiteral( "GeometryGroup" ) )
9017 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9018 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
9019 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
9020 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
9021 fcnScale, QStringLiteral( "GeometryGroup" ) )
9022 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9023 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
9024 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
9025 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
9026 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
9027 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
9028 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
9029 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
9030 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
9031 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
9032 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
9033 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9034 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9035 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
9036 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
9037 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
9038 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
9039 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
9040 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9041 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
9042 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9043 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
9044 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9045 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
9046 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9047 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
9048 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
9049 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
9050 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
9051 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9052 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
9053 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
9054 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9055 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
9056 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9057 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9058 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
9059 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9060 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9061 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9062 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9063 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9064 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
9065 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9066 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9067 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9068 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9069 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9070 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
9071 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9072 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
9073 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
9074 fcnExtend, QStringLiteral( "GeometryGroup" ) )
9075 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
9076 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
9077 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9078 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
9079 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, { QStringLiteral( "String" ), QStringLiteral( "GeometryGroup" ) } )
9080 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
9081 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9082 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9083 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
9084 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9085 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9086 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
9087 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
9088 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
9089 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
9090 {
9091 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
9092 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9093 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
9094 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
9095 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
9096 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
9097 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
9098 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
9099 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
9100 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
9101 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
9102 {
9103 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9104 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9105 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9106 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9107 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
9108 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
9109 {
9110 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9111 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9112 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9113 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9114 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9115 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9116 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9117 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
9118 {
9119 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9120 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9121 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9122 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9123 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
9124 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
9125 {
9126 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9127 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9128 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9129 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9130 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9131 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9132 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9133 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
9134 {
9135 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9136 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9137 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9138 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9139 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
9140 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
9141 {
9142 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9143 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9144 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9145 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9146 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9147 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9148 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9149 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
9150 {
9151 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9152 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
9153 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
9154 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
9155 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
9156 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
9157 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
9158 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
9159 {
9160 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9161 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
9162 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
9163 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
9164 {
9165 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9166 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9167 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
9168 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
9169 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
9170 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
9171 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
9172 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
9173 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
9174 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
9175 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
9176 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
9177 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9178 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
9179#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9180 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9181 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
9182 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
9183#endif
9184 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
9185 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9186 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
9187 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
9188 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9189 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
9190 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
9191 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9192 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9193 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
9194 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9195 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9196 fcnDifference, QStringLiteral( "GeometryGroup" ) )
9197 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9198 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9199 fcnDistance, QStringLiteral( "GeometryGroup" ) )
9200 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9201 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
9202 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
9203 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9204 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9205 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
9206 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9207 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9208 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
9209 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9210 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9211 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9212 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9213 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9214 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9215 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9216 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
9217 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
9218 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9219 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9220 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
9221 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9222 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
9223 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
9224 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
9225 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9226 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9227 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
9228 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
9229 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9230 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
9231 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9232 fcnZMax, QStringLiteral( "GeometryGroup" ) )
9233 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9234 fcnZMin, QStringLiteral( "GeometryGroup" ) )
9235 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9236 fcnMMax, QStringLiteral( "GeometryGroup" ) )
9237 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9238 fcnMMin, QStringLiteral( "GeometryGroup" ) )
9239 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9240 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
9241 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9242 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
9243
9244
9245 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9246 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
9247 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
9248 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
9249
9250 orderPartsFunc->setIsStaticFunction(
9251 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9252 {
9253 const QList< QgsExpressionNode *> argList = node->args()->list();
9254 for ( QgsExpressionNode *argNode : argList )
9255 {
9256 if ( !argNode->isStatic( parent, context ) )
9257 return false;
9258 }
9259
9260 if ( node->args()->count() > 1 )
9261 {
9262 QgsExpressionNode *argNode = node->args()->at( 1 );
9263
9264 QString expString = argNode->eval( parent, context ).toString();
9265
9266 QgsExpression e( expString );
9267
9268 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9269 return true;
9270 }
9271
9272 return true;
9273 } );
9274
9275 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9276 {
9277 if ( node->args()->count() > 1 )
9278 {
9279 QgsExpressionNode *argNode = node->args()->at( 1 );
9280 QString expression = argNode->eval( parent, context ).toString();
9282 e.prepare( context );
9283 context->setCachedValue( expression, QVariant::fromValue( e ) );
9284 }
9285 return true;
9286 }
9287 );
9288 functions << orderPartsFunc;
9289
9290 functions
9291 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9292 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9293 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
9294 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9295 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9296 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
9297 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9298 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
9299 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9300 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9301 fcnLineInterpolatePointByM, QStringLiteral( "GeometryGroup" ) )
9302 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9303 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
9304 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9305 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
9306 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9307 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9308 fcnLineLocateM, QStringLiteral( "GeometryGroup" ) )
9309 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9310 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
9311 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9312 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
9313 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9314 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
9315
9316
9317 // **Record** functions
9318
9319 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
9320 idFunc->setIsStatic( false );
9321 functions << idFunc;
9322
9323 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
9324 currentFeatureFunc->setIsStatic( false );
9325 functions << currentFeatureFunc;
9326
9327 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" ) );
9328 uuidFunc->setIsStatic( false );
9329 functions << uuidFunc;
9330
9331 functions
9332 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
9333 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9334 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
9335 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
9336 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
9337 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9338 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
9339 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
9340
9341 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
9342 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9343 attributesFunc->setIsStatic( false );
9344 functions << attributesFunc;
9345 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
9346 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9347 representAttributesFunc->setIsStatic( false );
9348 functions << representAttributesFunc;
9349
9350 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
9351 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9352 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9353 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9354 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9355 validateFeature->setIsStatic( false );
9356 functions << validateFeature;
9357
9358 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
9359 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
9360 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9361 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9362 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9363 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9364 validateAttribute->setIsStatic( false );
9365 functions << validateAttribute;
9366
9368 QStringLiteral( "maptip" ),
9369 -1,
9370 fcnFeatureMaptip,
9371 QStringLiteral( "Record and Attributes" ),
9372 QString(),
9373 false,
9374 QSet<QString>()
9375 );
9376 maptipFunc->setIsStatic( false );
9377 functions << maptipFunc;
9378
9380 QStringLiteral( "display_expression" ),
9381 -1,
9382 fcnFeatureDisplayExpression,
9383 QStringLiteral( "Record and Attributes" ),
9384 QString(),
9385 false,
9386 QSet<QString>()
9387 );
9388 displayFunc->setIsStatic( false );
9389 functions << displayFunc;
9390
9392 QStringLiteral( "is_selected" ),
9393 -1,
9394 fcnIsSelected,
9395 QStringLiteral( "Record and Attributes" ),
9396 QString(),
9397 false,
9398 QSet<QString>()
9399 );
9400 isSelectedFunc->setIsStatic( false );
9401 functions << isSelectedFunc;
9402
9403 functions
9405 QStringLiteral( "num_selected" ),
9406 -1,
9407 fcnNumSelected,
9408 QStringLiteral( "Record and Attributes" ),
9409 QString(),
9410 false,
9411 QSet<QString>()
9412 );
9413
9414 functions
9416 QStringLiteral( "sqlite_fetch_and_increment" ),
9418 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
9419 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
9420 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
9421 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
9422 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
9423 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
9424 fcnSqliteFetchAndIncrement,
9425 QStringLiteral( "Record and Attributes" )
9426 );
9427
9428 // **CRS** functions
9429 functions
9430 << new QgsStaticExpressionFunction( QStringLiteral( "crs_to_authid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "crs" ) ), fcnCrsToAuthid, QStringLiteral( "CRS" ), QString(), true )
9431 << new QgsStaticExpressionFunction( QStringLiteral( "crs_from_text" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "definition" ) ), fcnCrsFromText, QStringLiteral( "CRS" ) );
9432
9433
9434 // **Fields and Values** functions
9435 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
9436
9437 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9438 {
9439 Q_UNUSED( context )
9440 if ( node->args()->count() == 1 )
9441 {
9442 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9443 if ( colRef )
9444 {
9445 return true;
9446 }
9447 else
9448 {
9449 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9450 return false;
9451 }
9452 }
9453 else if ( node->args()->count() == 2 )
9454 {
9455 return true;
9456 }
9457 else
9458 {
9459 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9460 return false;
9461 }
9462 }
9463 );
9464
9465 functions << representValueFunc;
9466
9467 // **General** functions
9468 functions
9469 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9470 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
9471 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
9472 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
9474 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9475 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
9476 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
9477 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
9479 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
9480 fcnMimeType, QStringLiteral( "General" ) )
9481 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9482 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
9483 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
9484
9485 // **var** function
9486 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
9487 varFunction->setIsStaticFunction(
9488 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9489 {
9490 /* A variable node is static if it has a static name and the name can be found at prepare
9491 * time and is tagged with isStatic.
9492 * It is not static if a variable is set during iteration or not tagged isStatic.
9493 * (e.g. geom_part variable)
9494 */
9495 if ( node->args()->count() > 0 )
9496 {
9497 QgsExpressionNode *argNode = node->args()->at( 0 );
9498
9499 if ( !argNode->isStatic( parent, context ) )
9500 return false;
9501
9502 const QString varName = argNode->eval( parent, context ).toString();
9503 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
9504 return false;
9505
9506 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9507 return scope ? scope->isStatic( varName ) : false;
9508 }
9509 return false;
9510 }
9511 );
9512 varFunction->setUsesGeometryFunction(
9513 []( const QgsExpressionNodeFunction * node ) -> bool
9514 {
9515 if ( node && node->args()->count() > 0 )
9516 {
9517 QgsExpressionNode *argNode = node->args()->at( 0 );
9518 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9519 {
9520 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
9521 return true;
9522 }
9523 }
9524 return false;
9525 }
9526 );
9527
9528 functions
9529 << varFunction;
9530
9531 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9532 evalTemplateFunction->setIsStaticFunction(
9533 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9534 {
9535 if ( node->args()->count() > 0 )
9536 {
9537 QgsExpressionNode *argNode = node->args()->at( 0 );
9538
9539 if ( argNode->isStatic( parent, context ) )
9540 {
9541 QString expString = argNode->eval( parent, context ).toString();
9542
9543 QgsExpression e( expString );
9544
9545 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9546 return true;
9547 }
9548 }
9549
9550 return false;
9551 } );
9552 functions << evalTemplateFunction;
9553
9554 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9555 evalFunc->setIsStaticFunction(
9556 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9557 {
9558 if ( node->args()->count() > 0 )
9559 {
9560 QgsExpressionNode *argNode = node->args()->at( 0 );
9561
9562 if ( argNode->isStatic( parent, context ) )
9563 {
9564 QString expString = argNode->eval( parent, context ).toString();
9565
9566 QgsExpression e( expString );
9567
9568 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9569 return true;
9570 }
9571 }
9572
9573 return false;
9574 } );
9575
9576 functions << evalFunc;
9577
9578 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9579 attributeFunc->setIsStaticFunction(
9580 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9581 {
9582 const QList< QgsExpressionNode *> argList = node->args()->list();
9583 for ( QgsExpressionNode *argNode : argList )
9584 {
9585 if ( !argNode->isStatic( parent, context ) )
9586 return false;
9587 }
9588
9589 if ( node->args()->count() == 1 )
9590 {
9591 // not static -- this is the variant which uses the current feature taken direct from the expression context
9592 return false;
9593 }
9594
9595 return true;
9596 } );
9597 functions << attributeFunc;
9598
9599 functions
9600 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9602 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9603 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9604
9605 // functions for arrays
9608 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9609 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9610 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9611 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9612 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9613 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9614 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9615 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9616 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9617 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9618 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9619 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9620 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9621 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9622 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9623 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9624 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9625 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9626 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9627 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9628 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9629 << 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 )
9630 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9631 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9632 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9633 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9634 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9635 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9636 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9637 << 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" ) )
9638 << 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" ) )
9639 << 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" ) )
9640 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9641
9642 //functions for maps
9643 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9644 << 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" ) )
9645 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9646 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9647 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9648 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9649 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9650 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9651 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9652 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9653 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9654 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9655 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9656 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9657 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9658 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9659 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9660 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9661 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9662 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9663 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9664
9665 ;
9666
9668
9669 //QgsExpression has ownership of all built-in functions
9670 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9671 {
9672 *sOwnedFunctions() << func;
9673 *sBuiltinFunctions() << func->name();
9674 sBuiltinFunctions()->append( func->aliases() );
9675 }
9676 }
9677 return functions;
9678}
9679
9680bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9681{
9682 int fnIdx = functionIndex( function->name() );
9683 if ( fnIdx != -1 )
9684 {
9685 return false;
9686 }
9687
9688 QMutexLocker locker( &sFunctionsMutex );
9689 sFunctions()->append( function );
9690 if ( transferOwnership )
9691 sOwnedFunctions()->append( function );
9692
9693 return true;
9694}
9695
9696bool QgsExpression::unregisterFunction( const QString &name )
9697{
9698 // You can never override the built in functions.
9699 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9700 {
9701 return false;
9702 }
9703 int fnIdx = functionIndex( name );
9704 if ( fnIdx != -1 )
9705 {
9706 QMutexLocker locker( &sFunctionsMutex );
9707 sFunctions()->removeAt( fnIdx );
9708 sFunctionIndexMap.clear();
9709 return true;
9710 }
9711 return false;
9712}
9713
9715{
9716 qDeleteAll( *sOwnedFunctions() );
9717 sOwnedFunctions()->clear();
9719
9720const QStringList &QgsExpression::BuiltinFunctions()
9721{
9722 if ( sBuiltinFunctions()->isEmpty() )
9723 {
9724 Functions(); // this method builds the gmBuiltinFunctions as well
9725 }
9726 return *sBuiltinFunctions();
9728
9730 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9731 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9732 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9733 QStringLiteral( "Arrays" ) )
9734{
9735
9736}
9737
9739{
9740 bool isStatic = false;
9741
9742 QgsExpressionNode::NodeList *args = node->args();
9744 if ( args->count() < 2 )
9745 return false;
9746
9747 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9748 {
9749 isStatic = true;
9750 }
9751 return isStatic;
9752}
9753
9755{
9756 Q_UNUSED( node )
9757 QVariantList result;
9758
9759 if ( args->count() < 2 )
9760 // error
9761 return result;
9762
9763 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9764
9765 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9766 std::unique_ptr< QgsExpressionContext > tempContext;
9767 if ( !subContext )
9768 {
9769 tempContext = std::make_unique< QgsExpressionContext >();
9770 subContext = tempContext.get();
9771 }
9772
9774 subContext->appendScope( subScope );
9775
9776 int i = 0;
9777 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
9778 {
9779 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9780 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), i, true ) );
9781 result << args->at( 1 )->eval( parent, subContext );
9782 }
9783
9784 if ( context )
9785 delete subContext->popScope();
9786
9787 return result;
9788}
9789
9790QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9792 // This is a dummy function, all the real handling is in run
9793 Q_UNUSED( values )
9794 Q_UNUSED( context )
9795 Q_UNUSED( parent )
9796 Q_UNUSED( node )
9797
9798 Q_ASSERT( false );
9799 return QVariant();
9800}
9801
9803{
9804 QgsExpressionNode::NodeList *args = node->args();
9805
9806 if ( args->count() < 2 )
9807 // error
9808 return false;
9809
9810 args->at( 0 )->prepare( parent, context );
9811
9812 QgsExpressionContext subContext;
9813 if ( context )
9814 subContext = *context;
9817 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9818 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), QVariant(), true ) );
9819 subContext.appendScope( subScope );
9820
9821 args->at( 1 )->prepare( parent, &subContext );
9822
9823 return true;
9824}
9827 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9828 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9829 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9830 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9831 QStringLiteral( "Arrays" ) )
9832{
9833
9834}
9835
9837{
9838 bool isStatic = false;
9839
9840 QgsExpressionNode::NodeList *args = node->args();
9842 if ( args->count() < 2 )
9843 return false;
9844
9845 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9846 {
9847 isStatic = true;
9848 }
9849 return isStatic;
9850}
9851
9853{
9854 Q_UNUSED( node )
9855 QVariantList result;
9856
9857 if ( args->count() < 2 )
9858 // error
9859 return result;
9860
9861 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9862
9863 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9864 std::unique_ptr< QgsExpressionContext > tempContext;
9865 if ( !subContext )
9866 {
9867 tempContext = std::make_unique< QgsExpressionContext >();
9868 subContext = tempContext.get();
9869 }
9870
9872 subContext->appendScope( subScope );
9873
9874 int limit = 0;
9875 if ( args->count() >= 3 )
9876 {
9877 const QVariant limitVar = args->at( 2 )->eval( parent, context );
9878
9879 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
9880 {
9881 limit = limitVar.toInt();
9882 }
9883 else
9884 {
9885 return result;
9886 }
9887 }
9888
9889 for ( const QVariant &value : array )
9890 {
9891 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
9892 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
9893 {
9894 result << value;
9895
9896 if ( limit > 0 && limit == result.size() )
9897 break;
9898 }
9899 }
9900
9901 if ( context )
9902 delete subContext->popScope();
9903
9904 return result;
9905}
9906
9907QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9909 // This is a dummy function, all the real handling is in run
9910 Q_UNUSED( values )
9911 Q_UNUSED( context )
9912 Q_UNUSED( parent )
9913 Q_UNUSED( node )
9914
9915 Q_ASSERT( false );
9916 return QVariant();
9917}
9918
9920{
9921 QgsExpressionNode::NodeList *args = node->args();
9922
9923 if ( args->count() < 2 )
9924 // error
9925 return false;
9926
9927 args->at( 0 )->prepare( parent, context );
9928
9929 QgsExpressionContext subContext;
9930 if ( context )
9931 subContext = *context;
9932
9934 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9935 subContext.appendScope( subScope );
9936
9937 args->at( 1 )->prepare( parent, &subContext );
9938
9939 return true;
9942 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
9943 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
9944 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
9945 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9946 QStringLiteral( "General" ) )
9947{
9948
9949}
9950
9952{
9953 bool isStatic = false;
9954
9955 QgsExpressionNode::NodeList *args = node->args();
9956
9957 if ( args->count() < 3 )
9958 return false;
9959
9960 // We only need to check if the node evaluation is static, if both - name and value - are static.
9961 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9962 {
9963 QVariant name = args->at( 0 )->eval( parent, context );
9964 QVariant value = args->at( 1 )->eval( parent, context );
9966 // Temporarily append a new scope to provide the variable
9967 appendTemporaryVariable( context, name.toString(), value );
9968 if ( args->at( 2 )->isStatic( parent, context ) )
9969 isStatic = true;
9970 popTemporaryVariable( context );
9971 }
9972
9973 return isStatic;
9974}
9975
9977{
9978 Q_UNUSED( node )
9979 QVariant result;
9980
9981 if ( args->count() < 3 )
9982 // error
9983 return result;
9984
9985 QVariant name = args->at( 0 )->eval( parent, context );
9986 QVariant value = args->at( 1 )->eval( parent, context );
9987
9988 const QgsExpressionContext *updatedContext = context;
9989 std::unique_ptr< QgsExpressionContext > tempContext;
9990 if ( !updatedContext )
9991 {
9992 tempContext = std::make_unique< QgsExpressionContext >();
9993 updatedContext = tempContext.get();
9995
9996 appendTemporaryVariable( updatedContext, name.toString(), value );
9997 result = args->at( 2 )->eval( parent, updatedContext );
9998
9999 if ( context )
10000 popTemporaryVariable( updatedContext );
10001
10002 return result;
10003}
10004
10005QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10007 // This is a dummy function, all the real handling is in run
10008 Q_UNUSED( values )
10009 Q_UNUSED( context )
10010 Q_UNUSED( parent )
10011 Q_UNUSED( node )
10012
10013 Q_ASSERT( false );
10014 return QVariant();
10015}
10016
10018{
10019 QgsExpressionNode::NodeList *args = node->args();
10020
10021 if ( args->count() < 3 )
10022 // error
10023 return false;
10024
10025 QVariant name = args->at( 0 )->prepare( parent, context );
10026 QVariant value = args->at( 1 )->prepare( parent, context );
10027
10028 const QgsExpressionContext *updatedContext = context;
10029 std::unique_ptr< QgsExpressionContext > tempContext;
10030 if ( !updatedContext )
10031 {
10032 tempContext = std::make_unique< QgsExpressionContext >();
10033 updatedContext = tempContext.get();
10034 }
10035
10036 appendTemporaryVariable( updatedContext, name.toString(), value );
10037 args->at( 2 )->prepare( parent, updatedContext );
10038
10039 if ( context )
10040 popTemporaryVariable( updatedContext );
10041
10042 return true;
10043}
10044
10045void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10046{
10047 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10048 delete updatedContext->popScope();
10049}
10050
10051void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10052{
10055
10056 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10057 updatedContext->appendScope( scope );
10058}
@ Left
Buffer to left of line.
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3201
@ ScaleDashOnly
Only dash lengths are adjusted.
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
@ ScaleGapOnly
Only gap lengths are adjusted.
@ Success
Operation succeeded.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
JoinStyle
Join styles for buffers.
Definition qgis.h:2084
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:5785
@ StdDev
Standard deviation.
@ NoStatistic
No statistic.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2071
@ Flat
Flat cap (in line with start/end of line)
@ Round
Round cap.
@ Square
Square cap (extends past start/end of line by buffer distance)
Aggregate
Available aggregates to calculate.
Definition qgis.h:5662
@ 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:3186
@ HalfDash
Start or finish the pattern with a half length dash.
@ HalfGap
Start or finish the pattern with a half length gap.
@ FullGap
Start or finish the pattern with a full gap.
@ FullDash
Start or finish the pattern with a full dash.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2130
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
@ PointM
PointM.
@ PointZ
PointZ.
@ GeometryCollection
GeometryCollection.
@ PointZM
PointZM.
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Handles the array_filter(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
Handles the array loopingarray_Foreach(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:45
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h: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.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:39
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
An abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
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 its value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static 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.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:746
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:139
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2814
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
A representation of the interval between two datetime values.
Definition qgsinterval.h: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:77
QString name
Definition qgsmaplayer.h:81
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:84
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:80
QgsLayerMetadata metadata
Definition qgsmaplayer.h:83
Qgis::LayerType type
Definition qgsmaplayer.h:87
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:90
Implementation of a geometry simplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:692
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:558
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:426
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:105
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:704
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
void grow(double delta)
Grows the rectangle in place by the specified amount.
double yMaximum
QgsPointXY center
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices)
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:48
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
Helper class for defining QgsExpression functions.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
Utility functions for working with strings.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition qgsstyle.cpp:501
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:146
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
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 Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:286
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6920
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6919
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6408
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6367
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QList< QgsExpressionFunction * > ExpressionFunctionList
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColorObject(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
Definition qgsgeometry.h:84
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:80
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition qgsgeometry.h:91
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
const QgsCoordinateReferenceSystem & crs
int precision
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:62
const QgsMapLayer * layer
Definition qgsogcutils.h:72
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:73
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30