QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
17#include <random>
18
20#include "qgscoordinateutils.h"
22#include "qgsexpressionutils.h"
24#include "qgsexiftools.h"
25#include "qgsfeaturerequest.h"
26#include "qgsgeos.h"
27#include "qgsstringutils.h"
28#include "qgsmultipoint.h"
29#include "qgsgeometryutils.h"
30#include "qgshstoreutils.h"
31#include "qgsmultilinestring.h"
32#include "qgslinestring.h"
33#include "qgscurvepolygon.h"
35#include "qgspolygon.h"
36#include "qgstriangle.h"
37#include "qgscurve.h"
38#include "qgsregularpolygon.h"
39#include "qgsquadrilateral.h"
40#include "qgsvariantutils.h"
41#include "qgsogcutils.h"
42#include "qgsdistancearea.h"
43#include "qgsgeometryengine.h"
45#include "qgssymbollayerutils.h"
46#include "qgsstyle.h"
47#include "qgsexception.h"
48#include "qgsmessagelog.h"
49#include "qgsrasterlayer.h"
50#include "qgsvectorlayer.h"
51#include "qgsvectorlayerutils.h"
52#include "qgsrasterbandstats.h"
53#include "qgscolorramp.h"
55#include "qgsfieldformatter.h"
57#include "qgsproviderregistry.h"
58#include "sqlite3.h"
59#include "qgstransaction.h"
60#include "qgsthreadingutils.h"
61#include "qgsapplication.h"
62#include "qgis.h"
64#include "qgsunittypes.h"
65#include "qgsspatialindex.h"
66#include "qgscolorrampimpl.h"
67
68#include <QMimeDatabase>
69#include <QProcessEnvironment>
70#include <QCryptographicHash>
71#include <QRegularExpression>
72#include <QUuid>
73#include <QUrlQuery>
74
75typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
76
78Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
80
83Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
84
85const QString QgsExpressionFunction::helpText() const
86{
87 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
88}
89
91{
92 Q_UNUSED( node )
93 // evaluate arguments
94 QVariantList argValues;
95 if ( args )
96 {
97 int arg = 0;
98 const QList< QgsExpressionNode * > argList = args->list();
99 for ( QgsExpressionNode *n : argList )
100 {
101 QVariant v;
102 if ( lazyEval() )
103 {
104 // Pass in the node for the function to eval as it needs.
105 v = QVariant::fromValue( n );
106 }
107 else
108 {
109 v = n->eval( parent, context );
111 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
112 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
113 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
114 }
115 argValues.append( v );
116 arg++;
117 }
118 }
119
120 return func( argValues, context, parent, node );
121}
122
124{
125 Q_UNUSED( node )
126 return true;
127}
128
130{
131 return QStringList();
132}
133
135{
136 Q_UNUSED( parent )
137 Q_UNUSED( context )
138 Q_UNUSED( node )
139 return false;
140}
141
143{
144 Q_UNUSED( parent )
145 Q_UNUSED( context )
146 Q_UNUSED( node )
147 return true;
148}
149
151{
152 Q_UNUSED( node )
153 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
154}
155
157{
158 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
159}
160
162{
163 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
164}
165
167{
168 return mHandlesNull;
169}
170
171// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
174 FcnEval fcn,
175 const QString &group,
176 const QString &helpText,
177 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
178 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
179 bool lazyEval,
180 const QStringList &aliases,
181 bool handlesNull )
182 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
183 , mFnc( fcn )
184 , mAliases( aliases )
185 , mUsesGeometry( false )
186 , mUsesGeometryFunc( usesGeometry )
187 , mReferencedColumnsFunc( referencedColumns )
188{
189}
191
193{
194 return mAliases;
195}
196
198{
199 if ( mUsesGeometryFunc )
200 return mUsesGeometryFunc( node );
201 else
202 return mUsesGeometry;
203}
204
205void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
206{
207 mUsesGeometryFunc = usesGeometry;
208}
209
211{
212 if ( mReferencedColumnsFunc )
213 return mReferencedColumnsFunc( node );
214 else
215 return mReferencedColumns;
216}
217
219{
220 if ( mIsStaticFunc )
221 return mIsStaticFunc( node, parent, context );
222 else
223 return mIsStatic;
224}
225
227{
228 if ( mPrepareFunc )
229 return mPrepareFunc( node, parent, context );
230
231 return true;
232}
233
235{
236 mIsStaticFunc = isStatic;
237}
238
240{
241 mIsStaticFunc = nullptr;
242 mIsStatic = isStatic;
243}
244
245void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
246{
247 mPrepareFunc = prepareFunc;
248}
249
251{
252 if ( node && node->args() )
253 {
254 const QList< QgsExpressionNode * > argList = node->args()->list();
255 for ( QgsExpressionNode *argNode : argList )
256 {
257 if ( !argNode->isStatic( parent, context ) )
258 return false;
259 }
260 }
261
262 return true;
263}
264
265static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
266{
267 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
268 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
269 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
270
271 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
272 return QVariant();
273
274 QVariantList array;
275 int length = 1;
276
277 array << start;
278 double current = start + step;
279 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
280 {
281 array << current;
282 current += step;
283 length++;
284 }
285
286 return array;
287}
288
289static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
290{
291 if ( !context )
292 return QVariant();
293
294 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
295
296 if ( name == QLatin1String( "feature" ) )
297 {
298 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
299 }
300 else if ( name == QLatin1String( "id" ) )
301 {
302 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
303 }
304 else if ( name == QLatin1String( "geometry" ) )
305 {
306 if ( !context->hasFeature() )
307 return QVariant();
308
309 const QgsFeature feature = context->feature();
310 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
311 }
312 else
313 {
314 return context->variable( name );
315 }
316}
317
318static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
319{
320 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
321 return QgsExpression::replaceExpressionText( templateString, context );
322}
323
324static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
325{
326 if ( !context )
327 return QVariant();
328
329 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
330 QgsExpression expression( expString );
331 return expression.evaluate( context );
332}
333
334static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
335{
336 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
337 return QVariant( std::sqrt( x ) );
338}
339
340static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341{
342 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343 return QVariant( std::fabs( val ) );
344}
345
346static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
347{
348 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
349 return ( deg * M_PI ) / 180;
350}
351static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
352{
353 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
354 return ( 180 * rad ) / M_PI;
355}
356static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
357{
358 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
359 return QVariant( std::sin( x ) );
360}
361static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
362{
363 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
364 return QVariant( std::cos( x ) );
365}
366static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
367{
368 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
369 return QVariant( std::tan( x ) );
370}
371static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
372{
373 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
374 return QVariant( std::asin( x ) );
375}
376static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
377{
378 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
379 return QVariant( std::acos( x ) );
380}
381static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
382{
383 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
384 return QVariant( std::atan( x ) );
385}
386static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
387{
388 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
389 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
390 return QVariant( std::atan2( y, x ) );
391}
392static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
393{
394 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
395 return QVariant( std::exp( x ) );
396}
397static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
398{
399 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
400 if ( x <= 0 )
401 return QVariant();
402 return QVariant( std::log( x ) );
403}
404static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 if ( x <= 0 )
408 return QVariant();
409 return QVariant( log10( x ) );
410}
411static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
412{
413 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
414 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
415 if ( x <= 0 || b <= 0 )
416 return QVariant();
417 return QVariant( std::log( x ) / std::log( b ) );
418}
419static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
420{
421 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
422 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
423 if ( max < min )
424 return QVariant();
425
426 std::random_device rd;
427 std::mt19937_64 generator( rd() );
428
429 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
430 {
431 quint32 seed;
432 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
433 {
434 // if seed can be converted to int, we use as is
435 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
436 }
437 else
438 {
439 // if not, we hash string representation to int
440 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
441 std::hash<std::string> hasher;
442 seed = hasher( seedStr.toStdString() );
443 }
444 generator.seed( seed );
445 }
446
447 // Return a random double in the range [min, max] (inclusive)
448 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
449 return QVariant( min + f * ( max - min ) );
450}
451static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
452{
453 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
454 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
455 if ( max < min )
456 return QVariant();
457
458 std::random_device rd;
459 std::mt19937_64 generator( rd() );
460
461 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
462 {
463 quint32 seed;
464 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
465 {
466 // if seed can be converted to int, we use as is
467 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
468 }
469 else
470 {
471 // if not, we hash string representation to int
472 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
473 std::hash<std::string> hasher;
474 seed = hasher( seedStr.toStdString() );
475 }
476 generator.seed( seed );
477 }
478
479 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
480 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
481 return QVariant( randomInteger );
482
483 // Prevent wrong conversion of QVariant. See #36412
484 return QVariant( int( randomInteger ) );
485}
486
487static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
488{
489 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
490 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
491 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
492 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
493 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
494
495 if ( domainMin >= domainMax )
496 {
497 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
498 return QVariant();
499 }
500
501 // outside of domain?
502 if ( val >= domainMax )
503 {
504 return rangeMax;
505 }
506 else if ( val <= domainMin )
507 {
508 return rangeMin;
509 }
510
511 // calculate linear scale
512 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
513 double c = rangeMin - ( domainMin * m );
514
515 // Return linearly scaled value
516 return QVariant( m * val + c );
517}
518
519static QVariant fcnExpScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
520{
521 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
522 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
523 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
524 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
525 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
526 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
527
528 if ( domainMin >= domainMax )
529 {
530 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
531 return QVariant();
532 }
533 if ( exponent <= 0 )
534 {
535 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
536 return QVariant();
537 }
538
539 // outside of domain?
540 if ( val >= domainMax )
541 {
542 return rangeMax;
543 }
544 else if ( val <= domainMin )
545 {
546 return rangeMin;
547 }
548
549 // Return exponentially scaled value
550 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
551}
552
553static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
554{
555 QVariant result( QVariant::Double );
556 double maxVal = std::numeric_limits<double>::quiet_NaN();
557 for ( const QVariant &val : values )
558 {
559 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
560 if ( std::isnan( maxVal ) )
561 {
562 maxVal = testVal;
563 }
564 else if ( !std::isnan( testVal ) )
565 {
566 maxVal = std::max( maxVal, testVal );
567 }
568 }
569
570 if ( !std::isnan( maxVal ) )
571 {
572 result = QVariant( maxVal );
573 }
574 return result;
575}
576
577static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
578{
579 QVariant result( QVariant::Double );
580 double minVal = std::numeric_limits<double>::quiet_NaN();
581 for ( const QVariant &val : values )
582 {
583 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
584 if ( std::isnan( minVal ) )
585 {
586 minVal = testVal;
587 }
588 else if ( !std::isnan( testVal ) )
589 {
590 minVal = std::min( minVal, testVal );
591 }
592 }
593
594 if ( !std::isnan( minVal ) )
595 {
596 result = QVariant( minVal );
597 }
598 return result;
599}
600
601static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
602{
603 //lazy eval, so we need to evaluate nodes now
604
605 //first node is layer id or name
606 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
608 QVariant value = node->eval( parent, context );
610
611 // TODO this expression function is NOT thread safe
613 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
615 if ( !vl )
616 {
617 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
618 return QVariant();
619 }
620
621 // second node is aggregate type
622 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
624 value = node->eval( parent, context );
626 bool ok = false;
627 QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
628 if ( !ok )
629 {
630 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
631 return QVariant();
632 }
633
634 // third node is subexpression (or field name)
635 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
637 QString subExpression = node->dump();
638
640 //optional forth node is filter
641 if ( values.count() > 3 )
642 {
643 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
645 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
646 if ( !nl || nl->value().isValid() )
647 parameters.filter = node->dump();
648 }
649
650 //optional fifth node is concatenator
651 if ( values.count() > 4 )
652 {
653 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
655 value = node->eval( parent, context );
657 parameters.delimiter = value.toString();
658 }
659
660 //optional sixth node is order by
661 QString orderBy;
662 if ( values.count() > 5 )
663 {
664 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
666 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
667 if ( !nl || nl->value().isValid() )
668 {
669 orderBy = node->dump();
670 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
671 }
672 }
673
674 QString aggregateError;
675 QVariant result;
676 if ( context )
677 {
678 QString cacheKey;
679 QgsExpression subExp( subExpression );
680 QgsExpression filterExp( parameters.filter );
681
682 bool isStatic = true;
683 if ( filterExp.referencedVariables().contains( QStringLiteral( "parent" ) )
684 || filterExp.referencedVariables().contains( QString() )
685 || subExp.referencedVariables().contains( QStringLiteral( "parent" ) )
686 || subExp.referencedVariables().contains( QString() ) )
687 {
688 isStatic = false;
689 }
690 else
691 {
692 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
693 for ( const QString &varName : refVars )
694 {
695 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
696 if ( scope && !scope->isStatic( varName ) )
697 {
698 isStatic = false;
699 break;
700 }
701 }
702 }
703
704 if ( !isStatic )
705 {
706 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
707 QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
708 }
709 else
710 {
711 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
712 }
713
714 if ( context->hasCachedValue( cacheKey ) )
715 {
716 return context->cachedValue( cacheKey );
717 }
718
719 QgsExpressionContext subContext( *context );
721 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
722 subContext.appendScope( subScope );
723 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
724
725 if ( ok )
726 {
727 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
728 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
729 // associated with it's calculation!
730 context->setCachedValue( cacheKey, result );
731 }
732 }
733 else
734 {
735 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
736 }
737 if ( !ok )
738 {
739 if ( !aggregateError.isEmpty() )
740 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
741 else
742 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
743 return QVariant();
744 }
745
746 return result;
747}
748
749static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
750{
751 if ( !context )
752 {
753 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
754 return QVariant();
755 }
756
757 // first step - find current layer
758
759 // TODO this expression function is NOT thread safe
761 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
763 if ( !vl )
764 {
765 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
766 return QVariant();
767 }
768
769 //lazy eval, so we need to evaluate nodes now
770
771 //first node is relation name
772 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
774 QVariant value = node->eval( parent, context );
776 QString relationId = value.toString();
777 // check relation exists
778 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId );
779 if ( !relation.isValid() || relation.referencedLayer() != vl )
780 {
781 // check for relations by name
782 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId );
783 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
784 {
785 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
786 return QVariant();
787 }
788 else
789 {
790 relation = relations.at( 0 );
791 }
792 }
793
794 QgsVectorLayer *childLayer = relation.referencingLayer();
795
796 // second node is aggregate type
797 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
799 value = node->eval( parent, context );
801 bool ok = false;
802 QgsAggregateCalculator::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
803 if ( !ok )
804 {
805 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
806 return QVariant();
807 }
808
809 //third node is subexpression (or field name)
810 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
812 QString subExpression = node->dump();
813
814 //optional fourth node is concatenator
816 if ( values.count() > 3 )
817 {
818 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
820 value = node->eval( parent, context );
822 parameters.delimiter = value.toString();
823 }
824
825 //optional fifth node is order by
826 QString orderBy;
827 if ( values.count() > 4 )
828 {
829 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
831 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
832 if ( !nl || nl->value().isValid() )
833 {
834 orderBy = node->dump();
835 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
836 }
837 }
838
839 if ( !context->hasFeature() )
840 return QVariant();
841 QgsFeature f = context->feature();
842
843 parameters.filter = relation.getRelatedFeaturesFilter( f );
844
845 QString cacheKey = QStringLiteral( "relagg:%1:%2:%3:%4:%5" ).arg( vl->id(),
846 QString::number( static_cast< int >( aggregate ) ),
847 subExpression,
848 parameters.filter,
849 orderBy );
850 if ( context->hasCachedValue( cacheKey ) )
851 return context->cachedValue( cacheKey );
852
853 QVariant result;
854 ok = false;
855
856
857 QgsExpressionContext subContext( *context );
858 QString error;
859 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
860
861 if ( !ok )
862 {
863 if ( !error.isEmpty() )
864 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
865 else
866 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
867 return QVariant();
868 }
869
870 // cache value
871 context->setCachedValue( cacheKey, result );
872 return result;
873}
874
875
876static QVariant fcnAggregateGeneric( QgsAggregateCalculator::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
877{
878 if ( !context )
879 {
880 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
881 return QVariant();
882 }
883
884 // first step - find current layer
885
886 // TODO this expression function is NOT thread safe
888 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
890 if ( !vl )
891 {
892 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
893 return QVariant();
894 }
895
896 //lazy eval, so we need to evaluate nodes now
897
898 //first node is subexpression (or field name)
899 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
901 QString subExpression = node->dump();
902
903 //optional second node is group by
904 QString groupBy;
905 if ( values.count() > 1 )
906 {
907 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
909 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
910 if ( !nl || nl->value().isValid() )
911 groupBy = node->dump();
912 }
913
914 //optional third node is filter
915 if ( values.count() > 2 )
916 {
917 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
919 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
920 if ( !nl || nl->value().isValid() )
921 parameters.filter = node->dump();
922 }
923
924 //optional order by node, if supported
925 QString orderBy;
926 if ( orderByPos >= 0 && values.count() > orderByPos )
927 {
928 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
930 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
931 if ( !nl || nl->value().isValid() )
932 {
933 orderBy = node->dump();
934 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
935 }
936 }
937
938 // build up filter with group by
939
940 // find current group by value
941 if ( !groupBy.isEmpty() )
942 {
943 QgsExpression groupByExp( groupBy );
944 QVariant groupByValue = groupByExp.evaluate( context );
945 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
946 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
947 QgsExpression::quotedValue( groupByValue ) );
948 if ( !parameters.filter.isEmpty() )
949 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
950 else
951 parameters.filter = groupByClause;
952 }
953
954 QgsExpression subExp( subExpression );
955 QgsExpression filterExp( parameters.filter );
956
957 bool isStatic = true;
958 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
959 for ( const QString &varName : refVars )
960 {
961 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
962 if ( scope && !scope->isStatic( varName ) )
963 {
964 isStatic = false;
965 break;
966 }
967 }
968
969 QString cacheKey;
970 if ( !isStatic )
971 {
972 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5%6:%7" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter,
973 QString::number( context->feature().id() ), QString::number( qHash( context->feature() ) ), orderBy );
974 }
975 else
976 {
977 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter, orderBy );
978 }
979
980 if ( context->hasCachedValue( cacheKey ) )
981 return context->cachedValue( cacheKey );
982
983 QVariant result;
984 bool ok = false;
985
986 QgsExpressionContext subContext( *context );
988 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
989 subContext.appendScope( subScope );
990 QString error;
991 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
992
993 if ( !ok )
994 {
995 if ( !error.isEmpty() )
996 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
997 else
998 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
999 return QVariant();
1000 }
1001
1002 // cache value
1003 context->setCachedValue( cacheKey, result );
1004 return result;
1005}
1006
1007
1008static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1009{
1010 return fcnAggregateGeneric( QgsAggregateCalculator::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1011}
1012
1013static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1014{
1015 return fcnAggregateGeneric( QgsAggregateCalculator::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1016}
1017
1018static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1019{
1020 return fcnAggregateGeneric( QgsAggregateCalculator::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1021}
1022
1023static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1024{
1025 return fcnAggregateGeneric( QgsAggregateCalculator::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1026}
1027
1028static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1029{
1030 return fcnAggregateGeneric( QgsAggregateCalculator::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1031}
1032
1033static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1034{
1035 return fcnAggregateGeneric( QgsAggregateCalculator::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1036}
1037
1038static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1039{
1040 return fcnAggregateGeneric( QgsAggregateCalculator::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1041}
1042
1043static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1044{
1045 return fcnAggregateGeneric( QgsAggregateCalculator::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1046}
1047
1048static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1049{
1050 return fcnAggregateGeneric( QgsAggregateCalculator::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1051}
1052
1053static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1054{
1055 return fcnAggregateGeneric( QgsAggregateCalculator::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1056}
1057
1058static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1059{
1060 return fcnAggregateGeneric( QgsAggregateCalculator::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1061}
1062
1063static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1064{
1065 return fcnAggregateGeneric( QgsAggregateCalculator::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1066}
1067
1068static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1069{
1070 return fcnAggregateGeneric( QgsAggregateCalculator::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1071}
1072
1073static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1074{
1075 return fcnAggregateGeneric( QgsAggregateCalculator::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1076}
1077
1078static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1079{
1080 return fcnAggregateGeneric( QgsAggregateCalculator::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1081}
1082
1083static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1084{
1085 return fcnAggregateGeneric( QgsAggregateCalculator::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1086}
1087
1088static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1089{
1090 return fcnAggregateGeneric( QgsAggregateCalculator::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1091}
1092
1093static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1094{
1095 return fcnAggregateGeneric( QgsAggregateCalculator::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1096}
1097
1098static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1099{
1101
1102 //fourth node is concatenator
1103 if ( values.count() > 3 )
1104 {
1105 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1107 QVariant value = node->eval( parent, context );
1109 parameters.delimiter = value.toString();
1110 }
1111
1112 return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent, 4 );
1113}
1114
1115static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1116{
1118
1119 //fourth node is concatenator
1120 if ( values.count() > 3 )
1121 {
1122 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1124 QVariant value = node->eval( parent, context );
1126 parameters.delimiter = value.toString();
1127 }
1128
1129 return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenateUnique, values, parameters, context, parent, 4 );
1130}
1131
1132static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1133{
1134 return fcnAggregateGeneric( QgsAggregateCalculator::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1135}
1136
1137static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1138{
1139 if ( !context )
1140 return QVariant();
1141
1142 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1143 bool ok = false;
1144 if ( QgsVariantUtils::isNull( scale ) )
1145 return QVariant();
1146
1147 const double v = scale.toDouble( &ok );
1148 if ( ok )
1149 return v;
1150 return QVariant();
1151}
1152
1153static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1154{
1155 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1156 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1157 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1158
1159 // force testValue to sit inside the range specified by the min and max value
1160 if ( testValue <= minValue )
1161 {
1162 return QVariant( minValue );
1163 }
1164 else if ( testValue >= maxValue )
1165 {
1166 return QVariant( maxValue );
1167 }
1168 else
1169 {
1170 return QVariant( testValue );
1171 }
1172}
1173
1174static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1175{
1176 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1177 return QVariant( std::floor( x ) );
1178}
1179
1180static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1181{
1182 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1183 return QVariant( std::ceil( x ) );
1184}
1185
1186static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1187{
1188 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1189}
1190static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1191{
1192 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1193}
1194static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1195{
1196 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1197}
1198
1199static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1200{
1201 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1202 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1203 if ( format.isEmpty() && !language.isEmpty() )
1204 {
1205 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1206 return QVariant( QDateTime() );
1207 }
1208
1209 if ( format.isEmpty() && language.isEmpty() )
1210 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1211
1212 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1213 QLocale locale = QLocale();
1214 if ( !language.isEmpty() )
1215 {
1216 locale = QLocale( language );
1217 }
1218
1219 QDateTime datetime = locale.toDateTime( datetimestring, format );
1220 if ( !datetime.isValid() )
1221 {
1222 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1223 datetime = QDateTime();
1224 }
1225 return QVariant( datetime );
1226}
1227
1228static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1229{
1230 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1231 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1232 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1233
1234 const QDate date( year, month, day );
1235 if ( !date.isValid() )
1236 {
1237 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1238 return QVariant();
1239 }
1240 return QVariant( date );
1241}
1242
1243static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1244{
1245 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1246 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1247 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1248
1249 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1250 if ( !time.isValid() )
1251 {
1252 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1253 return QVariant();
1254 }
1255 return QVariant( time );
1256}
1257
1258static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1259{
1260 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1261 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1262 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1263 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1264 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1265 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1266
1267 const QDate date( year, month, day );
1268 if ( !date.isValid() )
1269 {
1270 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1271 return QVariant();
1272 }
1273 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1274 if ( !time.isValid() )
1275 {
1276 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1277 return QVariant();
1278 }
1279 return QVariant( QDateTime( date, time ) );
1280}
1281
1282static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1283{
1284 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1285 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1286 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1287 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1288 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1289 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1290 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1291
1292 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1293}
1294
1295static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1296{
1297 for ( const QVariant &value : values )
1298 {
1299 if ( QgsVariantUtils::isNull( value ) )
1300 continue;
1301 return value;
1302 }
1303 return QVariant();
1304}
1305
1306static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1307{
1308 const QVariant val1 = values.at( 0 );
1309 const QVariant val2 = values.at( 1 );
1310
1311 if ( val1 == val2 )
1312 return QVariant();
1313 else
1314 return val1;
1315}
1316
1317static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1318{
1319 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1320 return QVariant( str.toLower() );
1321}
1322static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1323{
1324 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1325 return QVariant( str.toUpper() );
1326}
1327static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1328{
1329 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1330 QStringList elems = str.split( ' ' );
1331 for ( int i = 0; i < elems.size(); i++ )
1332 {
1333 if ( elems[i].size() > 1 )
1334 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1335 }
1336 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1337}
1338
1339static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1340{
1341 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1342 return QVariant( str.trimmed() );
1343}
1344
1345static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1346{
1347 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1348 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1349 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1350}
1351
1352static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1353{
1354 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1355 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1356 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1357}
1358
1359static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1360{
1361 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1362 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1363 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1364 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1365}
1366
1367static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1368{
1369 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1370 return QVariant( QgsStringUtils::soundex( string ) );
1371}
1372
1373static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1374{
1375 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1376 return QVariant( QString( character ) );
1377}
1378
1379static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1380{
1381 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1382
1383 if ( value.isEmpty() )
1384 {
1385 return QVariant();
1386 }
1387
1388 int res = value.at( 0 ).unicode();
1389 return QVariant( res );
1390}
1391
1392static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1393{
1394 if ( values.length() == 2 || values.length() == 3 )
1395 {
1396 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1397 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1398
1399 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1400
1401 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1402 }
1403
1404 return QVariant();
1405}
1406
1407static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1408{
1409 // two variants, one for geometry, one for string
1410 if ( values.at( 0 ).userType() == QMetaType::type( "QgsGeometry" ) )
1411 {
1412 //geometry variant
1413 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1414 if ( geom.type() != Qgis::GeometryType::Line )
1415 return QVariant();
1416
1417 return QVariant( geom.length() );
1418 }
1419
1420 //otherwise fall back to string variant
1421 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1422 return QVariant( str.length() );
1423}
1424
1425static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1426{
1427 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1428
1429 if ( geom.type() != Qgis::GeometryType::Line )
1430 return QVariant();
1431
1432 double totalLength = 0;
1433 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1434 {
1435 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1436 {
1437 totalLength += line->length3D();
1438 }
1439 else
1440 {
1441 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1442 totalLength += segmentized->length3D();
1443 }
1444 }
1445
1446 return totalLength;
1447}
1448
1449static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1450{
1451 if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
1452 {
1453 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1454 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1455 QVector< QPair< QString, QString > > mapItems;
1456
1457 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1458 {
1459 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1460 }
1461
1462 // larger keys should be replaced first since they may contain whole smaller keys
1463 std::sort( mapItems.begin(),
1464 mapItems.end(),
1465 []( const QPair< QString, QString > &pair1,
1466 const QPair< QString, QString > &pair2 )
1467 {
1468 return ( pair1.first.length() > pair2.first.length() );
1469 } );
1470
1471 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1472 {
1473 str = str.replace( it->first, it->second );
1474 }
1475
1476 return QVariant( str );
1477 }
1478 else if ( values.count() == 3 )
1479 {
1480 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1481 QVariantList before;
1482 QVariantList after;
1483 bool isSingleReplacement = false;
1484
1485 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).type() != QVariant::StringList )
1486 {
1487 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1488 }
1489 else
1490 {
1491 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1492 }
1493
1494 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1495 {
1496 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1497 isSingleReplacement = true;
1498 }
1499 else
1500 {
1501 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1502 }
1503
1504 if ( !isSingleReplacement && before.length() != after.length() )
1505 {
1506 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1507 return QVariant();
1508 }
1509
1510 for ( int i = 0; i < before.length(); i++ )
1511 {
1512 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1513 }
1514
1515 return QVariant( str );
1516 }
1517 else
1518 {
1519 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1520 return QVariant();
1521 }
1522}
1523
1524static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1525{
1526 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1527 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1528 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1529
1530 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1531 if ( !re.isValid() )
1532 {
1533 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1534 return QVariant();
1535 }
1536 return QVariant( str.replace( re, after ) );
1537}
1538
1539static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1540{
1541 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1542 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1543
1544 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1545 if ( !re.isValid() )
1546 {
1547 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1548 return QVariant();
1549 }
1550 return QVariant( ( str.indexOf( re ) + 1 ) );
1551}
1552
1553static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1554{
1555 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1556 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1557 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1558
1559 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1560 if ( !re.isValid() )
1561 {
1562 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1563 return QVariant();
1564 }
1565
1566 QRegularExpressionMatch matches = re.match( str );
1567 if ( matches.hasMatch() )
1568 {
1569 QVariantList array;
1570 QStringList list = matches.capturedTexts();
1571
1572 // Skip the first string to only return captured groups
1573 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1574 {
1575 array += ( !( *it ).isEmpty() ) ? *it : empty;
1576 }
1577
1578 return QVariant( array );
1579 }
1580 else
1581 {
1582 return QVariant();
1583 }
1584}
1585
1586static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1587{
1588 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1589 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1590
1591 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1592 if ( !re.isValid() )
1593 {
1594 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1595 return QVariant();
1596 }
1597
1598 // extract substring
1599 QRegularExpressionMatch match = re.match( str );
1600 if ( match.hasMatch() )
1601 {
1602 // return first capture
1603 if ( match.lastCapturedIndex() > 0 )
1604 {
1605 // a capture group was present, so use that
1606 return QVariant( match.captured( 1 ) );
1607 }
1608 else
1609 {
1610 // no capture group, so using all match
1611 return QVariant( match.captured( 0 ) );
1612 }
1613 }
1614 else
1615 {
1616 return QVariant( "" );
1617 }
1618}
1619
1620static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1621{
1622 QString uuid = QUuid::createUuid().toString();
1623 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1624 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1625 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1626 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1627 return uuid;
1628}
1629
1630static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1631{
1632 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1633 return QVariant();
1634
1635 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1636 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1637
1638 int len = 0;
1639 if ( values.at( 2 ).isValid() )
1640 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1641 else
1642 len = str.size();
1643
1644 if ( from < 0 )
1645 {
1646 from = str.size() + from;
1647 if ( from < 0 )
1648 {
1649 from = 0;
1650 }
1651 }
1652 else if ( from > 0 )
1653 {
1654 //account for the fact that substr() starts at 1
1655 from -= 1;
1656 }
1657
1658 if ( len < 0 )
1659 {
1660 len = str.size() + len - from;
1661 if ( len < 0 )
1662 {
1663 len = 0;
1664 }
1665 }
1666
1667 return QVariant( str.mid( from, len ) );
1668}
1669static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1670{
1671 FEAT_FROM_CONTEXT( context, f )
1672 // TODO: handling of 64-bit feature ids?
1673 return QVariant( static_cast< int >( f.id() ) );
1674}
1675
1676static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1677{
1678 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1679 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1680 bool foundLayer = false;
1681 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1682 {
1683 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1684 if ( !layer || !layer->dataProvider() )
1685 {
1686 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1687 return QVariant();
1688 }
1689
1690 if ( bandNb < 1 || bandNb > layer->bandCount() )
1691 {
1692 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1693 return QVariant();
1694 }
1695
1696 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1697 {
1698 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1699 return QVariant();
1700 }
1701
1702 QgsPointXY point = geom.asPoint();
1703 if ( geom.isMultipart() )
1704 {
1705 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1706 if ( multiPoint.count() == 1 )
1707 {
1708 point = multiPoint[0];
1709 }
1710 else
1711 {
1712 // if the geometry contains more than one part, return an undefined value
1713 return QVariant();
1714 }
1715 }
1716
1717 double value = layer->dataProvider()->sample( point, bandNb );
1718 return std::isnan( value ) ? QVariant() : value;
1719 },
1720 foundLayer );
1721
1722 if ( !foundLayer )
1723 {
1724 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1725 return QVariant();
1726 }
1727 else
1728 {
1729 return res;
1730 }
1731}
1732
1733static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1734{
1735 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1736 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1737
1738 bool foundLayer = false;
1739 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1740 {
1741 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1742 if ( !layer || !layer->dataProvider() )
1743 {
1744 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1745 return QVariant();
1746 }
1747
1748 if ( bandNb < 1 || bandNb > layer->bandCount() )
1749 {
1750 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1751 return QVariant();
1752 }
1753
1754 if ( std::isnan( value ) )
1755 {
1756 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1757 return QVariant();
1758 }
1759
1760 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1761 {
1762 return QVariant();
1763 }
1764
1765 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1766 if ( data.isEmpty() )
1767 {
1768 return QVariant();
1769 }
1770
1771 QVariantMap result;
1772 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1773 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1774 {
1775 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1776 if ( field.isColor() || field.isRamp() )
1777 {
1778 continue;
1779 }
1780 result.insert( fields.at( idx ).name, data.at( idx ) );
1781 }
1782
1783 return result;
1784 }, foundLayer );
1785
1786 if ( !foundLayer )
1787 {
1788 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1789 return QVariant();
1790 }
1791 else
1792 {
1793 return res;
1794 }
1795}
1796
1797static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1798{
1799 if ( !context )
1800 return QVariant();
1801
1802 return context->feature();
1803}
1804
1805static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1806{
1807 QgsFeature feature;
1808 QString attr;
1809 if ( values.size() == 1 )
1810 {
1811 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1812 feature = context->feature();
1813 }
1814 else if ( values.size() == 2 )
1815 {
1816 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1817 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1818 }
1819 else
1820 {
1821 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1822 return QVariant();
1823 }
1824
1825 return feature.attribute( attr );
1826}
1827
1828static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1829{
1830 QString table { R"html(
1831 <table>
1832 <thead>
1833 <th>%1</th>
1834 </thead>
1835 <tbody>
1836 <tr><td>%2</td></tr>
1837 </tbody>
1838 </table>)html" };
1839 QVariantMap dict;
1840 if ( values.size() == 1 )
1841 {
1842 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1843 }
1844 else
1845 {
1846 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1847 return QVariant();
1848 }
1849
1850 if ( dict.isEmpty() )
1851 {
1852 return QVariant();
1853 }
1854
1855 QStringList headers;
1856 QStringList cells;
1857
1858 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1859 {
1860 headers.push_back( it.key().toHtmlEscaped() );
1861 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1862 }
1863
1864 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1865}
1866
1867static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1868{
1869 QString table { R"html(
1870 <dl>
1871 %1
1872 </dl>)html" };
1873 QVariantMap dict;
1874 if ( values.size() == 1 )
1875 {
1876 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1877 }
1878 else
1879 {
1880 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1881 return QVariant();
1882 }
1883
1884 if ( dict.isEmpty() )
1885 {
1886 return QVariant();
1887 }
1888
1889 QString rows;
1890
1891 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1892 {
1893 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
1894 }
1895
1896 return table.arg( rows );
1897}
1898
1899static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1900{
1901 QVariant layer;
1902 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
1903 {
1904 layer = context->variable( QStringLiteral( "layer" ) );
1905 }
1906 else
1907 {
1908 //first node is layer id or name
1909 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1911 layer = node->eval( parent, context );
1913 }
1914
1915 QgsFeature feature;
1916 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
1917 {
1918 feature = context->feature();
1919 }
1920 else
1921 {
1922 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1923 }
1924
1926 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
1927 if ( strength == QLatin1String( "hard" ) )
1928 {
1930 }
1931 else if ( strength == QLatin1String( "soft" ) )
1932 {
1934 }
1935
1936 bool foundLayer = false;
1937 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
1938 {
1939 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
1940 if ( !layer )
1941 {
1942 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
1943 return QVariant();
1944 }
1945
1946 const QgsFields fields = layer->fields();
1947 bool valid = true;
1948 for ( int i = 0; i < fields.size(); i++ )
1949 {
1950 QStringList errors;
1951 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
1952 if ( !valid )
1953 {
1954 break;
1955 }
1956 }
1957
1958 return valid;
1959 }, foundLayer );
1960
1961 if ( !foundLayer )
1962 {
1963 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
1964 return QVariant();
1965 }
1966
1967 return res;
1968}
1969
1970static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1971{
1972 QVariant layer;
1973 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
1974 {
1975 layer = context->variable( QStringLiteral( "layer" ) );
1976 }
1977 else
1978 {
1979 //first node is layer id or name
1980 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
1982 layer = node->eval( parent, context );
1984 }
1985
1986 QgsFeature feature;
1987 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
1988 {
1989 feature = context->feature();
1990 }
1991 else
1992 {
1993 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
1994 }
1995
1997 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
1998 if ( strength == QLatin1String( "hard" ) )
1999 {
2001 }
2002 else if ( strength == QLatin1String( "soft" ) )
2003 {
2005 }
2006
2007 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2008
2009 bool foundLayer = false;
2010 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2011 {
2012 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2013 if ( !layer )
2014 {
2015 return QVariant();
2016 }
2017
2018 const int fieldIndex = layer->fields().indexFromName( attributeName );
2019 if ( fieldIndex == -1 )
2020 {
2021 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2022 return QVariant();
2023 }
2024
2025 QStringList errors;
2026 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2027 return valid;
2028 }, foundLayer );
2029
2030 if ( !foundLayer )
2031 {
2032 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2033 return QVariant();
2034 }
2035
2036 return res;
2037}
2038
2039static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2040{
2041 QgsFeature feature;
2042 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2043 {
2044 feature = context->feature();
2045 }
2046 else
2047 {
2048 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2049 }
2050
2051 const QgsFields fields = feature.fields();
2052 QVariantMap result;
2053 for ( int i = 0; i < fields.count(); ++i )
2054 {
2055 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2056 }
2057 return result;
2058}
2059
2060static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2061{
2062 QgsVectorLayer *layer = nullptr;
2063 QgsFeature feature;
2064
2065 // TODO this expression function is NOT thread safe
2067 if ( values.isEmpty() )
2068 {
2069 feature = context->feature();
2070 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2071 }
2072 else if ( values.size() == 1 )
2073 {
2074 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2075 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2076 }
2077 else if ( values.size() == 2 )
2078 {
2079 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2080 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2081 }
2082 else
2083 {
2084 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2085 return QVariant();
2086 }
2088
2089 if ( !layer )
2090 {
2091 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2092 return QVariant();
2093 }
2094
2095 if ( !feature.isValid() )
2096 {
2097 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2098 return QVariant();
2099 }
2100
2101 const QgsFields fields = feature.fields();
2102 QVariantMap result;
2103 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2104 {
2105 const QString fieldName { fields.at( fieldIndex ).name() };
2106 const QVariant attributeVal = feature.attribute( fieldIndex );
2107 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2108 if ( context && context->hasCachedValue( cacheValueKey ) )
2109 {
2110 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2111 }
2112 else
2113 {
2114 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2116 QVariant cache;
2117 if ( context )
2118 {
2119 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2120
2121 if ( !context->hasCachedValue( cacheKey ) )
2122 {
2123 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2124 context->setCachedValue( cacheKey, cache );
2125 }
2126 else
2127 {
2128 cache = context->cachedValue( cacheKey );
2129 }
2130 }
2131 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2132
2133 result.insert( fields.at( fieldIndex ).name(), value );
2134
2135 if ( context )
2136 {
2137 context->setCachedValue( cacheValueKey, value );
2138 }
2139
2140 }
2141 }
2142 return result;
2143}
2144
2145static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2146{
2147 QgsVectorLayer *layer = nullptr;
2148 QgsFeature feature;
2149 bool evaluate = true;
2150
2151 // TODO this expression function is NOT thread safe
2153 if ( values.isEmpty() )
2154 {
2155 feature = context->feature();
2156 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2157 }
2158 else if ( values.size() == 1 )
2159 {
2160 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2161 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2162 }
2163 else if ( values.size() == 2 )
2164 {
2165 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2166 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2167 }
2168 else if ( values.size() == 3 )
2169 {
2170 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2171 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2172 evaluate = values.value( 2 ).toBool();
2173 }
2174 else
2175 {
2176 if ( isMaptip )
2177 {
2178 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2179 }
2180 else
2181 {
2182 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2183 }
2184 return QVariant();
2185 }
2186
2187 if ( !layer )
2188 {
2189 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2190 return QVariant( );
2191 }
2193
2194 if ( !feature.isValid() )
2195 {
2196 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2197 return QVariant( );
2198 }
2199
2200 if ( ! evaluate )
2201 {
2202 if ( isMaptip )
2203 {
2204 return layer->mapTipTemplate();
2205 }
2206 else
2207 {
2208 return layer->displayExpression();
2209 }
2210 }
2211
2212 QgsExpressionContext subContext( *context );
2213 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2214 subContext.setFeature( feature );
2215
2216 if ( isMaptip )
2217 {
2218 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2219 }
2220 else
2221 {
2222 QgsExpression exp( layer->displayExpression() );
2223 exp.prepare( &subContext );
2224 return exp.evaluate( &subContext ).toString();
2225 }
2226}
2227
2228static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2229{
2230 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2231}
2232
2233static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2234{
2235 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2236}
2237
2238static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2239{
2240 QgsFeature feature;
2241 QVariant layer;
2242 if ( values.isEmpty() )
2243 {
2244 feature = context->feature();
2245 layer = context->variable( QStringLiteral( "layer" ) );
2246 }
2247 else if ( values.size() == 1 )
2248 {
2249 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2250 layer = context->variable( QStringLiteral( "layer" ) );
2251 }
2252 else if ( values.size() == 2 )
2253 {
2254 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2255 layer = values.at( 0 );
2256 }
2257 else
2258 {
2259 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2260 return QVariant();
2261 }
2262
2263 bool foundLayer = false;
2264 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2265 {
2266 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2267 if ( !layer || !feature.isValid() )
2268 {
2269 return QVariant( QVariant::Bool );
2270 }
2271
2272 return layer->selectedFeatureIds().contains( feature.id() );
2273 }, foundLayer );
2274 if ( !foundLayer )
2275 return QVariant( QVariant::Bool );
2276 else
2277 return res;
2278}
2279
2280static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2281{
2282 QVariant layer;
2283
2284 if ( values.isEmpty() )
2285 layer = context->variable( QStringLiteral( "layer" ) );
2286 else if ( values.count() == 1 )
2287 layer = values.at( 0 );
2288 else
2289 {
2290 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2291 return QVariant();
2292 }
2293
2294 bool foundLayer = false;
2295 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2296 {
2297 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2298 if ( !layer )
2299 {
2300 return QVariant( QVariant::LongLong );
2301 }
2302
2303 return layer->selectedFeatureCount();
2304 }, foundLayer );
2305 if ( !foundLayer )
2306 return QVariant( QVariant::LongLong );
2307 else
2308 return res;
2309}
2310
2311static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2312{
2313 static QMap<QString, qlonglong> counterCache;
2314 QVariant functionResult;
2315
2316 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2317 {
2318 QString database;
2319
2320 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2321
2322 if ( layer )
2323 {
2324 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2325 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2326 if ( database.isEmpty() )
2327 {
2328 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2329 }
2330 }
2331 else
2332 {
2333 database = databaseArgument;
2334 }
2335
2336 const QString table = values.at( 1 ).toString();
2337 const QString idColumn = values.at( 2 ).toString();
2338 const QString filterAttribute = values.at( 3 ).toString();
2339 const QVariant filterValue = values.at( 4 ).toString();
2340 const QVariantMap defaultValues = values.at( 5 ).toMap();
2341
2342 // read from database
2344 sqlite3_statement_unique_ptr sqliteStatement;
2345
2346 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2347 {
2348 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2349 functionResult = QVariant();
2350 return;
2351 }
2352
2353 QString errorMessage;
2354 QString currentValSql;
2355
2356 qlonglong nextId = 0;
2357 bool cachedMode = false;
2358 bool valueRetrieved = false;
2359
2360 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2361
2362 // Running in transaction mode, check for cached value first
2363 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2364 {
2365 cachedMode = true;
2366
2367 auto cachedCounter = counterCache.find( cacheString );
2368
2369 if ( cachedCounter != counterCache.end() )
2370 {
2371 qlonglong &cachedValue = cachedCounter.value();
2372 nextId = cachedValue;
2373 nextId += 1;
2374 cachedValue = nextId;
2375 valueRetrieved = true;
2376 }
2377 }
2378
2379 // Either not in cached mode or no cached value found, obtain from DB
2380 if ( !cachedMode || !valueRetrieved )
2381 {
2382 int result = SQLITE_ERROR;
2383
2384 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2385 if ( !filterAttribute.isNull() )
2386 {
2387 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2388 }
2389
2390 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2391
2392 if ( result == SQLITE_OK )
2393 {
2394 nextId = 0;
2395 if ( sqliteStatement.step() == SQLITE_ROW )
2396 {
2397 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2398 }
2399
2400 // If in cached mode: add value to cache and connect to transaction
2401 if ( cachedMode && result == SQLITE_OK )
2402 {
2403 counterCache.insert( cacheString, nextId );
2404
2405 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2406 {
2407 counterCache.remove( cacheString );
2408 } );
2409 }
2410 valueRetrieved = true;
2411 }
2412 }
2413
2414 if ( valueRetrieved )
2415 {
2416 QString upsertSql;
2417 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2418 QStringList cols;
2419 QStringList vals;
2420 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2421 vals << QgsSqliteUtils::quotedValue( nextId );
2422
2423 if ( !filterAttribute.isNull() )
2424 {
2425 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2426 vals << QgsSqliteUtils::quotedValue( filterValue );
2427 }
2428
2429 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2430 {
2431 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2432 vals << iter.value().toString();
2433 }
2434
2435 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2436 upsertSql += QLatin1String( " VALUES " );
2437 upsertSql += '(' + vals.join( ',' ) + ')';
2438
2439 int result = SQLITE_ERROR;
2440 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2441 {
2442 QgsTransaction *transaction = layer->dataProvider()->transaction();
2443 if ( transaction->executeSql( upsertSql, errorMessage ) )
2444 {
2445 result = SQLITE_OK;
2446 }
2447 }
2448 else
2449 {
2450 result = sqliteDb.exec( upsertSql, errorMessage );
2451 }
2452 if ( result == SQLITE_OK )
2453 {
2454 functionResult = QVariant( nextId );
2455 return;
2456 }
2457 else
2458 {
2459 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2460 functionResult = QVariant();
2461 return;
2462 }
2463 }
2464
2465 functionResult = QVariant();
2466 };
2467
2468 bool foundLayer = false;
2469 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2470 {
2471 fetchAndIncrementFunc( layer, QString() );
2472 }, foundLayer );
2473 if ( !foundLayer )
2474 {
2475 const QString databasePath = values.at( 0 ).toString();
2476 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2477 {
2478 fetchAndIncrementFunc( nullptr, databasePath );
2479 } );
2480 }
2481
2482 return functionResult;
2483}
2484
2485static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2486{
2487 QString concat;
2488 for ( const QVariant &value : values )
2489 {
2490 if ( !QgsVariantUtils::isNull( value ) )
2491 concat += QgsExpressionUtils::getStringValue( value, parent );
2492 }
2493 return concat;
2494}
2495
2496static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2497{
2498 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2499 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2500}
2501
2502static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2503{
2504 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2505 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2506 return string.right( pos );
2507}
2508
2509static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2510{
2511 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2512 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2513 return string.left( pos );
2514}
2515
2516static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2517{
2518 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2519 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2520 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2521 return string.leftJustified( length, fill.at( 0 ), true );
2522}
2523
2524static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2525{
2526 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2527 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2528 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2529 return string.rightJustified( length, fill.at( 0 ), true );
2530}
2531
2532static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2533{
2534 if ( values.size() < 1 )
2535 {
2536 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2537 return QVariant();
2538 }
2539
2540 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2541 for ( int n = 1; n < values.length(); n++ )
2542 {
2543 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2544 }
2545 return string;
2546}
2547
2548
2549static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2550{
2551 return QVariant( QDateTime::currentDateTime() );
2552}
2553
2554static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2555{
2556 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2557 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2558 if ( format.isEmpty() && !language.isEmpty() )
2559 {
2560 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2561 return QVariant( QDate() );
2562 }
2563
2564 if ( format.isEmpty() && language.isEmpty() )
2565 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2566
2567 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2568 QLocale locale = QLocale();
2569 if ( !language.isEmpty() )
2570 {
2571 locale = QLocale( language );
2572 }
2573
2574 QDate date = locale.toDate( datestring, format );
2575 if ( !date.isValid() )
2576 {
2577 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2578 date = QDate();
2579 }
2580 return QVariant( date );
2581}
2582
2583static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2584{
2585 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2586 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2587 if ( format.isEmpty() && !language.isEmpty() )
2588 {
2589 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2590 return QVariant( QTime() );
2591 }
2592
2593 if ( format.isEmpty() && language.isEmpty() )
2594 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2595
2596 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2597 QLocale locale = QLocale();
2598 if ( !language.isEmpty() )
2599 {
2600 locale = QLocale( language );
2601 }
2602
2603 QTime time = locale.toTime( timestring, format );
2604 if ( !time.isValid() )
2605 {
2606 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2607 time = QTime();
2608 }
2609 return QVariant( time );
2610}
2611
2612static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2613{
2614 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2615}
2616
2617/*
2618 * DMS functions
2619 */
2620
2621static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2622{
2623 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2624 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2625 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2626
2627 QString formatString;
2628 if ( values.count() > 3 )
2629 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2630
2631 QgsCoordinateFormatter::FormatFlags flags = QgsCoordinateFormatter::FormatFlags();
2632 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2633 {
2635 }
2636 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2637 {
2639 }
2640 else if ( ! formatString.isEmpty() )
2641 {
2642 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2643 return QVariant();
2644 }
2645
2646 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2647 {
2648 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2649 }
2650 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2651 {
2652 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2653 }
2654 else
2655 {
2656 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2657 return QVariant();
2658 }
2659}
2660
2661static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2662{
2664 return floatToDegreeFormat( format, values, context, parent, node );
2665}
2666
2667static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2668{
2669 double value = 0.0;
2670 bool ok = false;
2671 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2672
2673 return ok ? QVariant( value ) : QVariant();
2674}
2675
2676static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2677{
2679 return floatToDegreeFormat( format, values, context, parent, node );
2680}
2681
2682static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2683{
2684 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2685 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2686 qint64 seconds = d2.secsTo( d1 );
2687 return QVariant::fromValue( QgsInterval( seconds ) );
2688}
2689
2690static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2691{
2692 if ( !values.at( 0 ).canConvert<QDate>() )
2693 return QVariant();
2694
2695 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2696 if ( !date.isValid() )
2697 return QVariant();
2698
2699 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2700 // (to match PostgreSQL behavior)
2701 return date.dayOfWeek() % 7;
2702}
2703
2704static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2705{
2706 QVariant value = values.at( 0 );
2707 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2708 if ( inter.isValid() )
2709 {
2710 return QVariant( inter.days() );
2711 }
2712 else
2713 {
2714 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2715 return QVariant( d1.date().day() );
2716 }
2717}
2718
2719static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2720{
2721 QVariant value = values.at( 0 );
2722 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2723 if ( inter.isValid() )
2724 {
2725 return QVariant( inter.years() );
2726 }
2727 else
2728 {
2729 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2730 return QVariant( d1.date().year() );
2731 }
2732}
2733
2734static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2735{
2736 QVariant value = values.at( 0 );
2737 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2738 if ( inter.isValid() )
2739 {
2740 return QVariant( inter.months() );
2741 }
2742 else
2743 {
2744 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2745 return QVariant( d1.date().month() );
2746 }
2747}
2748
2749static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2750{
2751 QVariant value = values.at( 0 );
2752 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2753 if ( inter.isValid() )
2754 {
2755 return QVariant( inter.weeks() );
2756 }
2757 else
2758 {
2759 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2760 return QVariant( d1.date().weekNumber() );
2761 }
2762}
2763
2764static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2765{
2766 QVariant value = values.at( 0 );
2767 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2768 if ( inter.isValid() )
2769 {
2770 return QVariant( inter.hours() );
2771 }
2772 else
2773 {
2774 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2775 return QVariant( t1.hour() );
2776 }
2777}
2778
2779static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2780{
2781 QVariant value = values.at( 0 );
2782 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2783 if ( inter.isValid() )
2784 {
2785 return QVariant( inter.minutes() );
2786 }
2787 else
2788 {
2789 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2790 return QVariant( t1.minute() );
2791 }
2792}
2793
2794static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2795{
2796 QVariant value = values.at( 0 );
2797 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2798 if ( inter.isValid() )
2799 {
2800 return QVariant( inter.seconds() );
2801 }
2802 else
2803 {
2804 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2805 return QVariant( t1.second() );
2806 }
2807}
2808
2809static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2810{
2811 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2812 if ( dt.isValid() )
2813 {
2814 return QVariant( dt.toMSecsSinceEpoch() );
2815 }
2816 else
2817 {
2818 return QVariant();
2819 }
2820}
2821
2822static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2823{
2824 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2825 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2826 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2827}
2828
2829static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2830{
2831 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2832 if ( parent->hasEvalError() )
2833 {
2834 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2835 return QVariant();
2836 }
2837 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2838 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2839}
2840
2841static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2843 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2844 if ( parent->hasEvalError() )
2845 {
2846 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2847 return QVariant();
2848 }
2849 bool ok;
2850 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2851}
2852
2853#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2854 if ( !(f).hasGeometry() ) \
2855 return QVariant(); \
2856 QgsGeometry g = (f).geometry(); \
2857 if ( (g).type() != (geomtype) ) \
2858 return QVariant();
2859
2860static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2861{
2862 FEAT_FROM_CONTEXT( context, f )
2863 ENSURE_GEOM_TYPE( f, g, Qgis::GeometryType::Point )
2864 if ( g.isMultipart() )
2865 {
2866 return g.asMultiPoint().at( 0 ).x();
2867 }
2868 else
2869 {
2870 return g.asPoint().x();
2871 }
2872}
2873
2874static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2875{
2876 FEAT_FROM_CONTEXT( context, f )
2877 ENSURE_GEOM_TYPE( f, g, Qgis::GeometryType::Point )
2878 if ( g.isMultipart() )
2879 {
2880 return g.asMultiPoint().at( 0 ).y();
2881 }
2882 else
2883 {
2884 return g.asPoint().y();
2885 }
2886}
2887
2888static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2889{
2890 FEAT_FROM_CONTEXT( context, f )
2891 ENSURE_GEOM_TYPE( f, g, Qgis::GeometryType::Point )
2892
2893 if ( g.isEmpty() )
2894 return QVariant();
2895
2896 const QgsAbstractGeometry *abGeom = g.constGet();
2897
2898 if ( g.isEmpty() || !abGeom->is3D() )
2899 return QVariant();
2900
2901 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
2902 {
2903 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
2904 if ( point )
2905 return point->z();
2906 }
2907 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
2908 {
2909 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
2910 {
2911 if ( collection->numGeometries() > 0 )
2912 {
2913 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
2914 return point->z();
2915 }
2916 }
2917 }
2918
2919 return QVariant();
2920}
2921
2922static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2923{
2924 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2925 if ( geom.isNull() )
2926 return QVariant();
2927
2928 bool isValid = geom.isGeosValid();
2929
2930 return QVariant( isValid );
2931}
2932
2933static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2934{
2935 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2936 if ( geom.isNull() )
2937 return QVariant();
2938
2939 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
2940#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
2942#else
2944#endif
2945 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
2947 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
2949
2950 const bool keepCollapsed = values.value( 2 ).toBool();
2951
2952 QgsGeometry valid;
2953 try
2954 {
2955 valid = geom.makeValid( method, keepCollapsed );
2956 }
2957 catch ( QgsNotSupportedException & )
2958 {
2959 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
2960 return QVariant();
2961 }
2962
2963 return QVariant::fromValue( valid );
2964}
2965
2966static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2967{
2968 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2969 if ( geom.isNull() )
2970 return QVariant();
2971
2972 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
2973 QVariantList array;
2974 for ( int i = 0; i < multiGeom.size(); ++i )
2975 {
2976 array += QVariant::fromValue( multiGeom.at( i ) );
2977 }
2978
2979 return array;
2980}
2981
2982static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2983{
2984 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2985 if ( geom.isNull() )
2986 return QVariant();
2987
2988 //if single point, return the point's x coordinate
2989 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
2990 {
2991 return geom.asPoint().x();
2992 }
2993
2994 //otherwise return centroid x
2995 QgsGeometry centroid = geom.centroid();
2996 QVariant result( centroid.asPoint().x() );
2997 return result;
2998}
2999
3000static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3001{
3002 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3003 if ( geom.isNull() )
3004 return QVariant();
3005
3006 //if single point, return the point's y coordinate
3007 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3008 {
3009 return geom.asPoint().y();
3010 }
3011
3012 //otherwise return centroid y
3013 QgsGeometry centroid = geom.centroid();
3014 QVariant result( centroid.asPoint().y() );
3015 return result;
3016}
3017
3018static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3019{
3020 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3021 if ( geom.isNull() )
3022 return QVariant(); //or 0?
3023
3024 if ( !geom.constGet()->is3D() )
3025 return QVariant();
3026
3027 //if single point, return the point's z coordinate
3028 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3029 {
3030 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3031 if ( point )
3032 return point->z();
3033 }
3034 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3035 {
3036 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3037 {
3038 if ( collection->numGeometries() == 1 )
3039 {
3040 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3041 return point->z();
3042 }
3043 }
3044 }
3045
3046 return QVariant();
3047}
3048
3049static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3050{
3051 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3052 if ( geom.isNull() )
3053 return QVariant(); //or 0?
3054
3055 if ( !geom.constGet()->isMeasure() )
3056 return QVariant();
3057
3058 //if single point, return the point's m value
3059 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3060 {
3061 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3062 if ( point )
3063 return point->m();
3064 }
3065 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3066 {
3067 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3068 {
3069 if ( collection->numGeometries() == 1 )
3070 {
3071 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3072 return point->m();
3073 }
3074 }
3075 }
3076
3077 return QVariant();
3078}
3079
3080static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3081{
3082 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3083
3084 if ( geom.isNull() )
3085 return QVariant();
3086
3087 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3088
3089 if ( idx < 0 )
3090 {
3091 //negative idx
3092 int count = geom.constGet()->nCoordinates();
3093 idx = count + idx;
3094 }
3095 else
3096 {
3097 //positive idx is 1 based
3098 idx -= 1;
3099 }
3100
3101 QgsVertexId vId;
3102 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3103 {
3104 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3105 return QVariant();
3106 }
3107
3108 QgsPoint point = geom.constGet()->vertexAt( vId );
3109 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3110}
3111
3112static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3113{
3114 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3115
3116 if ( geom.isNull() )
3117 return QVariant();
3118
3119 QgsVertexId vId;
3120 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3121 {
3122 return QVariant();
3123 }
3124
3125 QgsPoint point = geom.constGet()->vertexAt( vId );
3126 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3127}
3128
3129static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3130{
3131 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3132
3133 if ( geom.isNull() )
3134 return QVariant();
3135
3136 QgsVertexId vId;
3137 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3138 {
3139 return QVariant();
3140 }
3141
3142 QgsPoint point = geom.constGet()->vertexAt( vId );
3143 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3144}
3145
3146static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3147{
3148 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3149
3150 if ( geom.isNull() )
3151 return QVariant();
3152
3153 bool ignoreClosing = false;
3154 if ( values.length() > 1 )
3155 {
3156 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3157 }
3158
3159 QgsMultiPoint *mp = new QgsMultiPoint();
3160
3161 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3162 for ( const QgsRingSequence &part : sequence )
3163 {
3164 for ( const QgsPointSequence &ring : part )
3165 {
3166 bool skipLast = false;
3167 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3168 {
3169 skipLast = true;
3170 }
3171
3172 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3173 {
3174 mp->addGeometry( ring.at( i ).clone() );
3175 }
3176 }
3177 }
3178
3179 return QVariant::fromValue( QgsGeometry( mp ) );
3180}
3181
3182static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3183{
3184 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3185
3186 if ( geom.isNull() )
3187 return QVariant();
3188
3189 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3190
3191 //OK, now we have a complete list of segmentized lines from the geometry
3193 for ( QgsLineString *line : linesToProcess )
3194 {
3195 for ( int i = 0; i < line->numPoints() - 1; ++i )
3196 {
3198 segment->setPoints( QgsPointSequence()
3199 << line->pointN( i )
3200 << line->pointN( i + 1 ) );
3201 ml->addGeometry( segment );
3202 }
3203 delete line;
3204 }
3205
3206 return QVariant::fromValue( QgsGeometry( ml ) );
3207}
3208
3209static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3210{
3211 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3212
3213 if ( geom.isNull() )
3214 return QVariant();
3215
3216 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3217 if ( !curvePolygon && geom.isMultipart() )
3218 {
3219 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3220 {
3221 if ( collection->numGeometries() == 1 )
3222 {
3223 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3224 }
3225 }
3226 }
3227
3228 if ( !curvePolygon )
3229 return QVariant();
3230
3231 //idx is 1 based
3232 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3233
3234 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3235 return QVariant();
3236
3237 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3238 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3239 return result;
3240}
3241
3242static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3243{
3244 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3245
3246 if ( geom.isNull() )
3247 return QVariant();
3248
3249 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3250 if ( !collection )
3251 return QVariant();
3252
3253 //idx is 1 based
3254 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3255
3256 if ( idx < 0 || idx >= collection->numGeometries() )
3257 return QVariant();
3258
3259 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3260 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3261 return result;
3262}
3263
3264static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3265{
3266 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3267
3268 if ( geom.isNull() )
3269 return QVariant();
3270
3271 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3272 if ( !boundary )
3273 return QVariant();
3274
3275 return QVariant::fromValue( QgsGeometry( boundary ) );
3276}
3277
3278static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3279{
3280 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3281
3282 if ( geom.isNull() )
3283 return QVariant();
3284
3285 QgsGeometry merged = geom.mergeLines();
3286 if ( merged.isNull() )
3287 return QVariant();
3288
3289 return QVariant::fromValue( merged );
3290}
3291
3292static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3293{
3294 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3295 if ( geom.isNull() )
3296 return QVariant();
3297
3298 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3299 if ( geom2.isNull() )
3300 return QVariant();
3301
3302 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3303 if ( sharedPaths.isNull() )
3304 return QVariant();
3305
3306 return QVariant::fromValue( sharedPaths );
3307}
3308
3309
3310static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3311{
3312 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3313
3314 if ( geom.isNull() )
3315 return QVariant();
3316
3317 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3318
3319 QgsGeometry simplified = geom.simplify( tolerance );
3320 if ( simplified.isNull() )
3321 return QVariant();
3322
3323 return simplified;
3324}
3325
3326static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3327{
3328 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3329
3330 if ( geom.isNull() )
3331 return QVariant();
3332
3333 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3334
3336
3337 QgsGeometry simplified = simplifier.simplify( geom );
3338 if ( simplified.isNull() )
3339 return QVariant();
3340
3341 return simplified;
3342}
3343
3344static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3345{
3346 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3347
3348 if ( geom.isNull() )
3349 return QVariant();
3350
3351 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3352 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3353 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3354 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3355
3356 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3357 if ( smoothed.isNull() )
3358 return QVariant();
3359
3360 return smoothed;
3361}
3362
3363static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3364{
3365 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3366
3367 if ( geom.isNull() )
3368 return QVariant();
3369
3370 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3371 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3372 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3373
3374 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3375 if ( waved.isNull() )
3376 return QVariant();
3377
3378 return waved;
3379}
3380
3381static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3382{
3383 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3384
3385 if ( geom.isNull() )
3386 return QVariant();
3387
3388 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3389 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3390 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3391 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3392 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3393
3394 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3395 minAmplitude, maxAmplitude, seed );
3396 if ( waved.isNull() )
3397 return QVariant();
3398
3399 return waved;
3400}
3401
3402static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3403{
3404 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3405
3406 if ( geom.isNull() )
3407 return QVariant();
3408
3409 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3410 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3411 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3412
3413 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3414 if ( waved.isNull() )
3415 return QVariant();
3416
3417 return waved;
3418}
3419
3420static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3421{
3422 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3423
3424 if ( geom.isNull() )
3425 return QVariant();
3426
3427 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3428 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3429 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3430 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3431 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3432
3433 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3434 minAmplitude, maxAmplitude, seed );
3435 if ( waved.isNull() )
3436 return QVariant();
3437
3438 return waved;
3439}
3440
3441static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3442{
3443 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3444
3445 if ( geom.isNull() )
3446 return QVariant();
3447
3448 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3449 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3450 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3451
3452 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3453 if ( waved.isNull() )
3454 return QVariant();
3455
3456 return waved;
3457}
3458
3459static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3460{
3461 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3462
3463 if ( geom.isNull() )
3464 return QVariant();
3465
3466 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3467 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3468 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3469 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3470 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3471
3472 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3473 minAmplitude, maxAmplitude, seed );
3474 if ( waved.isNull() )
3475 return QVariant();
3476
3477 return waved;
3478}
3479
3480static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3481{
3482 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3483
3484 if ( geom.isNull() )
3485 return QVariant();
3486
3487 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3488 QVector< double > dashPattern;
3489 dashPattern.reserve( pattern.size() );
3490 for ( const QVariant &value : std::as_const( pattern ) )
3491 {
3492 bool ok = false;
3493 double v = value.toDouble( &ok );
3494 if ( ok )
3495 {
3496 dashPattern << v;
3497 }
3498 else
3499 {
3500 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3501 return QgsGeometry();
3502 }
3503 }
3504
3505 if ( dashPattern.size() % 2 != 0 )
3506 {
3507 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3508 return QgsGeometry();
3509 }
3510
3511 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3513 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3515 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3517 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3519 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3521 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3523 else
3524 {
3525 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3526 return QgsGeometry();
3527 }
3528
3529 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3531 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3533 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3535 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3537 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3539 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3541 else
3542 {
3543 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3544 return QgsGeometry();
3545 }
3546
3547 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3549 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3551 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3553 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3555 else
3556 {
3557 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3558 return QgsGeometry();
3559 }
3560
3561 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3562
3563 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3564 if ( result.isNull() )
3565 return QVariant();
3566
3567 return result;
3568}
3569
3570static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3571{
3572 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3573
3574 if ( geom.isNull() )
3575 return QVariant();
3576
3577 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3578 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3579 if ( densified.isNull() )
3580 return QVariant();
3581
3582 return densified;
3583}
3584
3585static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3586{
3587 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3588
3589 if ( geom.isNull() )
3590 return QVariant();
3591
3592 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3593 const QgsGeometry densified = geom.densifyByDistance( distance );
3594 if ( densified.isNull() )
3595 return QVariant();
3596
3597 return densified;
3598}
3599
3600static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3601{
3602 QVariantList list;
3603 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3604 {
3605 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3606 }
3607 else
3608 {
3609 list = values;
3610 }
3611
3612 QVector< QgsGeometry > parts;
3613 parts.reserve( list.size() );
3614 for ( const QVariant &value : std::as_const( list ) )
3615 {
3616 if ( value.userType() == QMetaType::type( "QgsGeometry" ) )
3617 {
3618 parts << value.value<QgsGeometry>();
3619 }
3620 else
3621 {
3622 parent->setEvalErrorString( QStringLiteral( "Cannot convert to geometry" ) );
3623 return QgsGeometry();
3624 }
3625 }
3626
3627 return QgsGeometry::collectGeometry( parts );
3628}
3629
3630static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3631{
3632 if ( values.count() < 2 || values.count() > 4 )
3633 {
3634 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3635 return QVariant();
3636 }
3637
3638 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3639 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3640 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3641 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3642 switch ( values.count() )
3643 {
3644 case 2:
3645 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3646 case 3:
3647 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3648 case 4:
3649 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3650 }
3651 return QVariant(); //avoid warning
3652}
3653
3654static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3655{
3656 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3657 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3658 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3659 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3660}
3661
3662static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3663{
3664 if ( values.empty() )
3665 {
3666 return QVariant();
3667 }
3668
3669 QVector<QgsPoint> points;
3670 points.reserve( values.count() );
3671
3672 auto addPoint = [&points]( const QgsGeometry & geom )
3673 {
3674 if ( geom.isNull() )
3675 return;
3676
3677 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3678 return;
3679
3680 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3681 if ( !point )
3682 return;
3683
3684 points << *point;
3685 };
3686
3687 for ( const QVariant &value : values )
3688 {
3689 if ( value.type() == QVariant::List )
3690 {
3691 const QVariantList list = value.toList();
3692 for ( const QVariant &v : list )
3693 {
3694 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3695 }
3696 }
3697 else
3698 {
3699 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3700 }
3701 }
3702
3703 if ( points.count() < 2 )
3704 return QVariant();
3705
3706 return QgsGeometry( new QgsLineString( points ) );
3707}
3708
3709static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3710{
3711 if ( values.count() < 1 )
3712 {
3713 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3714 return QVariant();
3715 }
3716
3717 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3718
3719 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3720 return outerRing; // if it's already a polygon we have nothing to do
3721
3722 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3723 return QVariant();
3724
3725 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
3726
3727 const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
3728 if ( !exteriorRing && outerRing.isMultipart() )
3729 {
3730 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3731 {
3732 if ( collection->numGeometries() == 1 )
3733 {
3734 exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3735 }
3736 }
3737 }
3738
3739 if ( !exteriorRing )
3740 return QVariant();
3741
3742 polygon->setExteriorRing( exteriorRing->segmentize() );
3743
3744
3745 for ( int i = 1; i < values.count(); ++i )
3746 {
3747 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3748 if ( ringGeom.isNull() )
3749 continue;
3750
3751 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3752 continue;
3753
3754 const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
3755 if ( !ring && ringGeom.isMultipart() )
3756 {
3757 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3758 {
3759 if ( collection->numGeometries() == 1 )
3760 {
3761 ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3762 }
3763 }
3764 }
3765
3766 if ( !ring )
3767 continue;
3768
3769 polygon->addInteriorRing( ring->segmentize() );
3770 }
3771
3772 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3773}
3774
3775static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3776{
3777 std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3778 std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3779 lineString->clear();
3780
3781 for ( const QVariant &value : values )
3782 {
3783 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3784 if ( geom.isNull() )
3785 return QVariant();
3786
3787 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3788 return QVariant();
3789
3790 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3791 if ( !point && geom.isMultipart() )
3792 {
3793 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3794 {
3795 if ( collection->numGeometries() == 1 )
3796 {
3797 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3798 }
3799 }
3800 }
3801
3802 if ( !point )
3803 return QVariant();
3804
3805 lineString->addVertex( *point );
3806 }
3807
3808 tr->setExteriorRing( lineString.release() );
3809
3810 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3811}
3812
3813static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3814{
3815 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3816 if ( geom.isNull() )
3817 return QVariant();
3818
3819 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3820 return QVariant();
3821
3822 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3823 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3824
3825 if ( segment < 3 )
3826 {
3827 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3828 return QVariant();
3829 }
3830 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3831 if ( !point && geom.isMultipart() )
3832 {
3833 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3834 {
3835 if ( collection->numGeometries() == 1 )
3836 {
3837 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3838 }
3839 }
3840 }
3841 if ( !point )
3842 return QVariant();
3843
3844 QgsCircle circ( *point, radius );
3845 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3846}
3847
3848static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3849{
3850 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3851 if ( geom.isNull() )
3852 return QVariant();
3853
3854 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3855 return QVariant();
3856
3857 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3858 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3859 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3860 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3861 if ( segment < 3 )
3862 {
3863 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3864 return QVariant();
3865 }
3866 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3867 if ( !point && geom.isMultipart() )
3868 {
3869 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3870 {
3871 if ( collection->numGeometries() == 1 )
3872 {
3873 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3874 }
3875 }
3876 }
3877 if ( !point )
3878 return QVariant();
3879
3880 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3881 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3882}
3883
3884static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3885{
3886
3887 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3888 if ( pt1.isNull() )
3889 return QVariant();
3890
3891 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
3892 return QVariant();
3893
3894 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3895 if ( pt2.isNull() )
3896 return QVariant();
3897
3898 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
3899 return QVariant();
3900
3901 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3902 if ( nbEdges < 3 )
3903 {
3904 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
3905 return QVariant();
3906 }
3907
3908 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3910 {
3911 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
3912 return QVariant();
3913 }
3914
3915 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
3916 if ( !center && pt1.isMultipart() )
3917 {
3918 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
3919 {
3920 if ( collection->numGeometries() == 1 )
3921 {
3922 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3923 }
3924 }
3925 }
3926 if ( !center )
3927 return QVariant();
3928
3929 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
3930 if ( !corner && pt2.isMultipart() )
3931 {
3932 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
3933 {
3934 if ( collection->numGeometries() == 1 )
3935 {
3936 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3937 }
3938 }
3939 }
3940 if ( !corner )
3941 return QVariant();
3942
3943 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
3944
3945 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
3946
3947}
3948
3949static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3950{
3951 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3952 if ( pt1.isNull() )
3953 return QVariant();
3954 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
3955 return QVariant();
3956
3957 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3958 if ( pt2.isNull() )
3959 return QVariant();
3960 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
3961 return QVariant();
3962
3963 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3964 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3965 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
3966
3967 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
3968}
3969
3970static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3971{
3972 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3973 if ( pt1.isNull() )
3974 return QVariant();
3975 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
3976 return QVariant();
3977
3978 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3979 if ( pt2.isNull() )
3980 return QVariant();
3981 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
3982 return QVariant();
3983
3984 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
3985 if ( pt3.isNull() )
3986 return QVariant();
3987 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
3988 return QVariant();
3989
3990 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
3991 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
3992 {
3993 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
3994 return QVariant();
3995 }
3996 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
3997 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
3998 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
3999 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4000 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4001}
4002
4003static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4004{
4005 if ( geom.isNull() )
4006 return QVariant();
4007
4008 if ( idx < 0 )
4009 {
4010 idx += geom.constGet()->nCoordinates();
4011 }
4012 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4013 {
4014 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4015 return QVariant();
4016 }
4017 return QVariant::fromValue( geom.vertexAt( idx ) );
4018}
4019
4020// function used for the old $ style
4021static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4022{
4023 FEAT_FROM_CONTEXT( context, feature )
4024 const QgsGeometry geom = feature.geometry();
4025 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4026
4027 const QVariant v = pointAt( geom, idx, parent );
4028
4029 if ( !v.isNull() )
4030 return QVariant( v.value<QgsPoint>().x() );
4031 else
4032 return QVariant();
4033}
4034static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4035{
4036 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))
4037 {
4038 return fcnOldXat( values, f, parent, node );
4039 }
4040 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)
4041 {
4042 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4043 }
4044
4045 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4046 if ( geom.isNull() )
4047 {
4048 return QVariant();
4049 }
4050
4051 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4052
4053 const QVariant v = pointAt( geom, vertexNumber, parent );
4054 if ( !v.isNull() )
4055 return QVariant( v.value<QgsPoint>().x() );
4056 else
4057 return QVariant();
4058}
4059
4060// function used for the old $ style
4061static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4062{
4063 FEAT_FROM_CONTEXT( context, feature )
4064 const QgsGeometry geom = feature.geometry();
4065 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4066
4067 const QVariant v = pointAt( geom, idx, parent );
4068
4069 if ( !v.isNull() )
4070 return QVariant( v.value<QgsPoint>().y() );
4071 else
4072 return QVariant();
4073}
4074static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4075{
4076 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))
4077 {
4078 return fcnOldYat( values, f, parent, node );
4079 }
4080 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)
4081 {
4082 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4083 }
4084
4085 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4086 if ( geom.isNull() )
4087 {
4088 return QVariant();
4089 }
4090
4091 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4092
4093 const QVariant v = pointAt( geom, vertexNumber, parent );
4094 if ( !v.isNull() )
4095 return QVariant( v.value<QgsPoint>().y() );
4096 else
4097 return QVariant();
4098}
4099
4100static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4101{
4102 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4103 if ( geom.isNull() )
4104 {
4105 return QVariant();
4106 }
4107
4108 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4109
4110 const QVariant v = pointAt( geom, vertexNumber, parent );
4111 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4112 return QVariant( v.value<QgsPoint>().z() );
4113 else
4114 return QVariant();
4115}
4116
4117static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4118{
4119 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4120 if ( geom.isNull() )
4121 {
4122 return QVariant();
4123 }
4124
4125 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4126
4127 const QVariant v = pointAt( geom, vertexNumber, parent );
4128 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4129 return QVariant( v.value<QgsPoint>().m() );
4130 else
4131 return QVariant();
4132}
4133
4134
4135static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4136{
4137 if ( !context )
4138 return QVariant();
4139
4140 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4141 if ( context->hasGeometry() )
4142 return context->geometry();
4143 else
4144 {
4145 FEAT_FROM_CONTEXT( context, f )
4146 QgsGeometry geom = f.geometry();
4147 if ( !geom.isNull() )
4148 return QVariant::fromValue( geom );
4149 else
4150 return QVariant();
4151 }
4152}
4153
4154static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4155{
4156 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4157 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4158 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4159 return result;
4160}
4161
4162static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4163{
4164 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4165 if ( wkb.isNull() )
4166 return QVariant();
4167
4168 QgsGeometry geom;
4169 geom.fromWkb( wkb );
4170 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4171}
4172
4173static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4174{
4175 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4176 QgsOgcUtils::Context ogcContext;
4177 if ( context )
4178 {
4179 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4180 if ( mapLayerPtr )
4181 {
4182 ogcContext.layer = mapLayerPtr.data();
4183 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4184 }
4185 }
4186 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4187 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4188 return result;
4189}
4190
4191static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4192{
4193 FEAT_FROM_CONTEXT( context, f )
4194 ENSURE_GEOM_TYPE( f, g, Qgis::GeometryType::Polygon )
4195 QgsDistanceArea *calc = parent->geomCalculator();
4196 if ( calc )
4197 {
4198 double area = calc->measureArea( f.geometry() );
4199 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4200 return QVariant( area );
4201 }
4202 else
4203 {
4204 return QVariant( f.geometry().area() );
4205 }
4206}
4207
4208static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4209{
4210 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4211
4212 if ( geom.type() != Qgis::GeometryType::Polygon )
4213 return QVariant();
4214
4215 return QVariant( geom.area() );
4216}
4217
4218static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4219{
4220 FEAT_FROM_CONTEXT( context, f )
4221 ENSURE_GEOM_TYPE( f, g, Qgis::GeometryType::Line )
4222 QgsDistanceArea *calc = parent->geomCalculator();
4223 if ( calc )
4224 {
4225 double len = calc->measureLength( f.geometry() );
4226 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4227 return QVariant( len );
4228 }
4229 else
4230 {
4231 return QVariant( f.geometry().length() );
4232 }
4233}
4234
4235static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4236{
4237 FEAT_FROM_CONTEXT( context, f )
4238 ENSURE_GEOM_TYPE( f, g, Qgis::GeometryType::Polygon )
4239 QgsDistanceArea *calc = parent->geomCalculator();
4240 if ( calc )
4241 {
4242 double len = calc->measurePerimeter( f.geometry() );
4243 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4244 return QVariant( len );
4245 }
4246 else
4247 {
4248 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4249 }
4250}
4251
4252static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4253{
4254 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4255
4256 if ( geom.type() != Qgis::GeometryType::Polygon )
4257 return QVariant();
4258
4259 //length for polygons = perimeter
4260 return QVariant( geom.length() );
4261}
4262
4263static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4264{
4265 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4266 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4267}
4268
4269static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4270{
4271 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4272 if ( geom.isNull() )
4273 return QVariant();
4274
4275 return QVariant( geom.constGet()->partCount() );
4276}
4277
4278static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4279{
4280 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4281 if ( geom.isNull() )
4282 return QVariant();
4283
4284 return QVariant( geom.isMultipart() );
4285}
4286
4287static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4288{
4289 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4290
4291 if ( geom.isNull() )
4292 return QVariant();
4293
4294 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4295 if ( curvePolygon )
4296 return QVariant( curvePolygon->numInteriorRings() );
4297
4298 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4299 if ( collection )
4300 {
4301 //find first CurvePolygon in collection
4302 for ( int i = 0; i < collection->numGeometries(); ++i )
4303 {
4304 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4305 if ( !curvePolygon )
4306 continue;
4307
4308 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4309 }
4310 }
4311
4312 return QVariant();
4313}
4314
4315static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4316{
4317 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4318
4319 if ( geom.isNull() )
4320 return QVariant();
4321
4322 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4323 if ( curvePolygon )
4324 return QVariant( curvePolygon->ringCount() );
4325
4326 bool foundPoly = false;
4327 int ringCount = 0;
4328 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4329 if ( collection )
4330 {
4331 //find CurvePolygons in collection
4332 for ( int i = 0; i < collection->numGeometries(); ++i )
4333 {
4334 curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
4335 if ( !curvePolygon )
4336 continue;
4337
4338 foundPoly = true;
4339 ringCount += curvePolygon->ringCount();
4340 }
4341 }
4342
4343 if ( !foundPoly )
4344 return QVariant();
4345
4346 return QVariant( ringCount );
4347}
4348
4349static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4350{
4351 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4352 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4353 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4354 return result;
4355}
4356
4357static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4358{
4359 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4360 return QVariant::fromValue( geom.boundingBox().width() );
4361}
4362
4363static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4364{
4365 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4366 return QVariant::fromValue( geom.boundingBox().height() );
4367}
4368
4369static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4370{
4371 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4372 if ( geom.isNull() )
4373 return QVariant();
4374
4376}
4377
4378static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4379{
4380 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4381 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4382}
4383
4384static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4385{
4386 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4387 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4388}
4389
4390static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4391{
4392 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4393 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4394}
4395
4396static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4397{
4398 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4399 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4400}
4401
4402static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4403{
4404 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4405
4406 if ( geom.isNull() || geom.isEmpty( ) )
4407 return QVariant();
4408
4409 if ( !geom.constGet()->is3D() )
4410 return QVariant();
4411
4412 double max = std::numeric_limits< double >::lowest();
4413
4414 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4415 {
4416 double z = ( *it ).z();
4417
4418 if ( max < z )
4419 max = z;
4420 }
4421
4422 if ( max == std::numeric_limits< double >::lowest() )
4423 return QVariant( QVariant::Double );
4424
4425 return QVariant( max );
4426}
4427
4428static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4429{
4430 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4431
4432 if ( geom.isNull() || geom.isEmpty() )
4433 return QVariant();
4434
4435 if ( !geom.constGet()->is3D() )
4436 return QVariant();
4437
4438 double min = std::numeric_limits< double >::max();
4439
4440 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4441 {
4442 double z = ( *it ).z();
4443
4444 if ( z < min )
4445 min = z;
4446 }
4447
4448 if ( min == std::numeric_limits< double >::max() )
4449 return QVariant( QVariant::Double );
4450
4451 return QVariant( min );
4452}
4453
4454static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4455{
4456 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4457
4458 if ( geom.isNull() || geom.isEmpty() )
4459 return QVariant();
4460
4461 if ( !geom.constGet()->isMeasure() )
4462 return QVariant();
4463
4464 double min = std::numeric_limits< double >::max();
4465
4466 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4467 {
4468 double m = ( *it ).m();
4469
4470 if ( m < min )
4471 min = m;
4472 }
4473
4474 if ( min == std::numeric_limits< double >::max() )
4475 return QVariant( QVariant::Double );
4476
4477 return QVariant( min );
4478}
4479
4480static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4481{
4482 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4483
4484 if ( geom.isNull() || geom.isEmpty() )
4485 return QVariant();
4486
4487 if ( !geom.constGet()->isMeasure() )
4488 return QVariant();
4489
4490 double max = std::numeric_limits< double >::lowest();
4491
4492 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4493 {
4494 double m = ( *it ).m();
4495
4496 if ( max < m )
4497 max = m;
4498 }
4499
4500 if ( max == std::numeric_limits< double >::lowest() )
4501 return QVariant( QVariant::Double );
4502
4503 return QVariant( max );
4504}
4505
4506static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4507{
4508 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4509 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4510 if ( !curve )
4511 {
4512 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4513 return QVariant();
4514 }
4515
4516 return QVariant( curve->sinuosity() );
4517}
4518
4519static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4520{
4521 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4522 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4523 if ( !curve )
4524 {
4525 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4526 return QVariant();
4527 }
4528
4529 return QVariant( curve->straightDistance2d() );
4530}
4531
4532static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4533{
4534 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4535 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4536
4537 if ( !poly )
4538 {
4539 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4540 return QVariant();
4541 }
4542
4543 return QVariant( poly->roundness() );
4544}
4545
4546
4547
4548static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4549{
4550 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4551 if ( geom.isNull() )
4552 return QVariant();
4553
4554 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4555 flipped->swapXy();
4556 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4557}
4558
4559static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4560{
4561 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4562 if ( fGeom.isNull() )
4563 return QVariant();
4564
4565 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4566 if ( !curve && fGeom.isMultipart() )
4567 {
4568 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4569 {
4570 if ( collection->numGeometries() == 1 )
4571 {
4572 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4573 }
4574 }
4575 }
4576
4577 if ( !curve )
4578 return QVariant();
4579
4580 return QVariant::fromValue( curve->isClosed() );
4581}
4582
4583static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4584{
4585 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4586
4587 if ( geom.isNull() )
4588 return QVariant();
4589
4590 QVariant result;
4591 if ( !geom.isMultipart() )
4592 {
4593 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4594
4595 if ( !line )
4596 return QVariant();
4597
4598 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4599 closedLine->close();
4600
4601 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4602 }
4603 else
4604 {
4605 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4606
4607 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4608
4609 for ( int i = 0; i < collection->numGeometries(); ++i )
4610 {
4611 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4612 {
4613 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4614 closedLine->close();
4615
4616 closed->addGeometry( closedLine.release() );
4617 }
4618 }
4619 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4620 }
4621
4622 return result;
4623}
4624
4625static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4626{
4627 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4628 if ( fGeom.isNull() )
4629 return QVariant();
4630
4631 return QVariant::fromValue( fGeom.isEmpty() );
4632}
4633
4634static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4635{
4636 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4637 return QVariant::fromValue( true );
4638
4639 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4640 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4641}
4642
4643static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4644{
4645 if ( values.length() < 2 || values.length() > 3 )
4646 return QVariant();
4647
4648 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4649 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4650
4651 if ( fGeom.isNull() || sGeom.isNull() )
4652 return QVariant();
4653
4654 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4655
4656 if ( values.length() == 2 )
4657 {
4658 //two geometry arguments, return relation
4659 QString result = engine->relate( sGeom.constGet() );
4660 return QVariant::fromValue( result );
4661 }
4662 else
4663 {
4664 //three arguments, test pattern
4665 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4666 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4667 return QVariant::fromValue( result );
4668 }
4669}
4670
4671static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4672{
4673 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4674 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4675 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4676}
4677static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4678{
4679 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4680 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4681 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4682}
4683static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4684{
4685 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4686 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4687 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4688}
4689static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4690{
4691 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4692 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4693 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4694}
4695static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4696{
4697 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4698 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4699 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4700}
4701static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4702{
4703 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4704 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4705 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4706}
4707static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4708{
4709 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4710 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4711 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4712}
4713static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4714{
4715 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4716 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4717 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4718}
4719
4720static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4721{
4722 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4723 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4724 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4725 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4726 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4727 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4728
4729 Qgis::EndCapStyle capStyle = Qgis::EndCapStyle::Round;
4730 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4731 capStyle = Qgis::EndCapStyle::Flat;
4732 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4733 capStyle = Qgis::EndCapStyle::Square;
4734
4735 Qgis::JoinStyle joinStyle = Qgis::JoinStyle::Round;
4736 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4737 joinStyle = Qgis::JoinStyle::Miter;
4738 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4739 joinStyle = Qgis::JoinStyle::Bevel;
4740
4741 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4742 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4743 return result;
4744}
4745
4746static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4747{
4748 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4749 const QgsGeometry reoriented = fGeom.forceRHR();
4750 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4751}
4752
4753static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4754{
4755 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4756 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4757 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4758}
4759
4760static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4761{
4762 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4763 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4764 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4765}
4766
4767static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4768{
4769 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4770 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4771 if ( !pt && fGeom.isMultipart() )
4772 {
4773 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4774 {
4775 if ( collection->numGeometries() == 1 )
4776 {
4777 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4778 }
4779 }
4780 }
4781
4782 if ( !pt )
4783 {
4784 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4785 return QVariant();
4786 }
4787
4788 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4789 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4790 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4791 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4792
4793 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4794 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4795 return result;
4796}
4797
4798static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4799{
4800 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4801 if ( fGeom.type() != Qgis::GeometryType::Line )
4802 {
4803 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4804 return QVariant();
4805 }
4806
4807 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4808 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4809 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4810
4811 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4812 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4813 return result;
4814}
4815
4816static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4817{
4818 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4819 if ( fGeom.type() != Qgis::GeometryType::Line )
4820 {
4821 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4822 return QVariant();
4823 }
4824
4825 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4826
4827 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4828 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4829 return result;
4830}
4831
4832static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4833{
4834 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4835 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4836 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4837 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4838 if ( joinInt < 1 || joinInt > 3 )
4839 return QVariant();
4840 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4841
4842 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4843
4844 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4845 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4846 return result;
4847}
4848
4849static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4850{
4851 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4852 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4853 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4854
4855 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4856 if ( joinInt < 1 || joinInt > 3 )
4857 return QVariant();
4858 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4859
4860 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4861
4862 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
4863 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4864 return result;
4865}
4866
4867static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4868{
4869 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4870 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4871 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4872
4873 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
4874 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4875 return result;
4876}
4877
4878static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4879{
4880 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4881 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4882 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4883 fGeom.translate( dx, dy );
4884 return QVariant::fromValue( fGeom );
4885}
4886
4887static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4888{
4889 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4890 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4891 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
4892 : QgsGeometry();
4893 const bool perPart = values.value( 3 ).toBool();
4894
4895 if ( center.isNull() && perPart && fGeom.isMultipart() )
4896 {
4897 // no explicit center, rotating per part
4898 // (note that we only do this branch for multipart geometries -- for singlepart geometries
4899 // the result is equivalent to setting perPart as false anyway)
4900 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
4901 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
4902 {
4903 const QgsPointXY partCenter = ( *it )->boundingBox().center();
4904 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
4905 t.rotate( -rotation );
4906 t.translate( -partCenter.x(), -partCenter.y() );
4907 ( *it )->transform( t );
4908 }
4909 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
4910 }
4911 else
4912 {
4913 QgsPointXY pt;
4914 if ( center.isEmpty() )
4915 {
4916 // if center wasn't specified, use bounding box centroid
4917 pt = fGeom.boundingBox().center();
4918 }
4920 {
4921 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
4922 return QVariant();
4923 }
4924 else
4925 {
4926 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
4927 }
4928
4929 fGeom.rotate( rotation, pt );
4930 return QVariant::fromValue( fGeom );
4931 }
4932}
4933
4934static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4935{
4936 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4937 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4938 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4939 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
4940 : QgsGeometry();
4941
4942 QgsPointXY pt;
4943 if ( center.isNull() )
4944 {
4945 // if center wasn't specified, use bounding box centroid
4946 pt = fGeom.boundingBox().center();
4947 }
4949 {
4950 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
4951 return QVariant();
4952 }
4953 else
4954 {
4955 pt = center.asPoint();
4956 }
4957
4958 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
4959 t.scale( xScale, yScale );
4960 t.translate( -pt.x(), -pt.y() );
4961 fGeom.transform( t );
4962 return QVariant::fromValue( fGeom );
4963}
4964
4965static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4966{
4967 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4968 if ( fGeom.isNull() )
4969 {
4970 return QVariant();
4971 }
4972
4973 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4974 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4975
4976 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4977
4978 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4979 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4980
4981 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
4982 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
4983 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
4984 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
4985
4986 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
4987 {
4988 fGeom.get()->addZValue( 0 );
4989 }
4990 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
4991 {
4992 fGeom.get()->addMValue( 0 );
4993 }
4994
4995 QTransform transform;
4996 transform.translate( deltaX, deltaY );
4997 transform.rotate( rotationZ );
4998 transform.scale( scaleX, scaleY );
4999 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5000
5001 return QVariant::fromValue( fGeom );
5002}
5003
5004
5005static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5006{
5007 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5008 QgsGeometry geom = fGeom.centroid();
5009 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5010 return result;
5011}
5012static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5013{
5014 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5015 QgsGeometry geom = fGeom.pointOnSurface();
5016 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5017 return result;
5018}
5019
5020static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5021{
5022 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5023 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5024 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5025 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5026 return result;
5027}
5028
5029static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5030{
5031 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5032 QgsGeometry geom = fGeom.convexHull();
5033 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5034 return result;
5035}
5036
5037#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5038static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5039{
5040 try
5041 {
5042 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5043 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5044 const bool allowHoles = values.value( 2 ).toBool();
5045 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5046 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5047 return result;
5048 }
5049 catch ( QgsCsException &cse )
5050 {
5051 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5052 return QVariant();
5053 }
5054}
5055#endif
5056
5057static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5058{
5059 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5060 int segments = 36;
5061 if ( values.length() == 2 )
5062 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5063 if ( segments < 0 )
5064 {
5065 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5066 return QVariant();
5067 }
5068
5069 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5070 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5071 return result;
5072}
5073
5074static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5075{
5076 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5078 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5079 return result;
5080}
5081
5082static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5083{
5084 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5085
5086 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5087 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5088 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5089
5090 double area, angle, width, height;
5091 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5092
5093 if ( geom.isNull() )
5094 {
5095 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5096 return QVariant();
5097 }
5098 return angle;
5099}
5100
5101static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5102{
5103 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5104 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5105 QgsGeometry geom = fGeom.difference( sGeom );
5106 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5107 return result;
5108}
5109
5110static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5111{
5112 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5113 if ( fGeom.isNull() )
5114 return QVariant();
5115
5116 QVariant result;
5117 if ( !fGeom.isMultipart() )
5118 {
5119 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5120 if ( !curve )
5121 return QVariant();
5122
5123 QgsCurve *reversed = curve->reversed();
5124 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5125 }
5126 else
5127 {
5128 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5129 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5130 for ( int i = 0; i < collection->numGeometries(); ++i )
5131 {
5132 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5133 {
5134 reversed->addGeometry( curve->reversed() );
5135 }
5136 else
5137 {
5138 reversed->addGeometry( collection->geometryN( i )->clone() );
5139 }
5140 }
5141 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5142 }
5143 return result;
5144}
5145
5146static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5147{
5148 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5149 if ( fGeom.isNull() )
5150 return QVariant();
5151
5152 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5153 if ( !curvePolygon && fGeom.isMultipart() )
5154 {
5155 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5156 {
5157 if ( collection->numGeometries() == 1 )
5158 {
5159 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5160 }
5161 }
5162 }
5163
5164 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5165 return QVariant();
5166
5167 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5168 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5169 return result;
5170}
5171
5172static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5173{
5174 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5175 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5176 return QVariant( fGeom.distance( sGeom ) );
5177}
5178
5179static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5180{
5181 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5182 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5183
5184 double res = -1;
5185 if ( values.length() == 3 && values.at( 2 ).isValid() )
5186 {
5187 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5188 densify = std::clamp( densify, 0.0, 1.0 );
5189 res = g1.hausdorffDistanceDensify( g2, densify );
5190 }
5191 else
5192 {
5193 res = g1.hausdorffDistance( g2 );
5194 }
5195
5196 return res > -1 ? QVariant( res ) : QVariant();
5197}
5198
5199static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5200{
5201 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5202 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5203 QgsGeometry geom = fGeom.intersection( sGeom );
5204 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5205 return result;
5206}
5207static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5208{
5209 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5210 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5211 QgsGeometry geom = fGeom.symDifference( sGeom );
5212 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5213 return result;
5214}
5215static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5216{
5217 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5218 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5219 QgsGeometry geom = fGeom.combine( sGeom );
5220 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5221 return result;
5222}
5223
5224static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5225{
5226 if ( values.length() < 1 || values.length() > 2 )
5227 return QVariant();
5228
5229 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5230 int prec = 8;
5231 if ( values.length() == 2 )
5232 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5233 QString wkt = fGeom.asWkt( prec );
5234 return QVariant( wkt );
5235}
5236
5237static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5238{
5239 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5240 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5241}
5242
5243static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5244{
5245 if ( values.length() != 2 )
5246 {
5247 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5248 return QVariant();
5249 }
5250
5251 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5252 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5253
5254 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5255 if ( !pt1 && fGeom1.isMultipart() )
5256 {
5257 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5258 {
5259 if ( collection->numGeometries() == 1 )
5260 {
5261 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5262 }
5263 }
5264 }
5265
5266 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5267 if ( !pt2 && fGeom2.isMultipart() )
5268 {
5269 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5270 {
5271 if ( collection->numGeometries() == 1 )
5272 {
5273 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5274 }
5275 }
5276 }
5277
5278 if ( !pt1 || !pt2 )
5279 {
5280 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5281 return QVariant();
5282 }
5283
5284 // Code from PostGIS
5285 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5286 {
5287 if ( pt1->y() < pt2->y() )
5288 return 0.0;
5289 else if ( pt1->y() > pt2->y() )
5290 return M_PI;
5291 else
5292 return 0;
5293 }
5294
5295 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5296 {
5297 if ( pt1->x() < pt2->x() )
5298 return M_PI_2;
5299 else if ( pt1->x() > pt2->x() )
5300 return M_PI + ( M_PI_2 );
5301 else
5302 return 0;
5303 }
5304
5305 if ( pt1->x() < pt2->x() )
5306 {
5307 if ( pt1->y() < pt2->y() )
5308 {
5309 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5310 }
5311 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5312 {
5313 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5314 + ( M_PI_2 );
5315 }
5316 }
5317
5318 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5319 {
5320 if ( pt1->y() > pt2->y() )
5321 {
5322 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5323 + M_PI;
5324 }
5325 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5326 {
5327 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5328 + ( M_PI + ( M_PI_2 ) );
5329 }
5330 }
5331}
5332
5333static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5334{
5335 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5336
5338 {
5339 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5340 return QVariant();
5341 }
5342
5343 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5344 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5345 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5346
5347 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5348 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5349
5350 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5351}
5352
5353static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5354{
5355 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5356 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5357
5358 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5359 if ( !pt1 && fGeom1.isMultipart() )
5360 {
5361 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5362 {
5363 if ( collection->numGeometries() == 1 )
5364 {
5365 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5366 }
5367 }
5368 }
5369 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5370 if ( !pt2 && fGeom2.isMultipart() )
5371 {
5372 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5373 {
5374 if ( collection->numGeometries() == 1 )
5375 {
5376 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5377 }
5378 }
5379 }
5380
5381 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5382 !pt1 || !pt2 )
5383 {
5384 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5385 return QVariant();
5386 }
5387
5388 return pt1->inclination( *pt2 );
5389
5390}
5391
5392static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5393{
5394 if ( values.length() != 3 )
5395 return QVariant();
5396
5397 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5398 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5399 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5400
5401 QgsGeometry geom = fGeom.extrude( x, y );
5402
5403 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5404 return result;
5405}
5406
5407static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5408{
5409 if ( values.length() < 2 )
5410 return QVariant();
5411
5412 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5413
5414 if ( !fGeom.isMultipart() )
5415 return values.at( 0 );
5416
5417 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5418 QVariant cachedExpression;
5419 if ( ctx )
5420 cachedExpression = ctx->cachedValue( expString );
5421 QgsExpression expression;
5422
5423 if ( cachedExpression.isValid() )
5424 {
5425 expression = cachedExpression.value<QgsExpression>();
5426 }
5427 else
5428 expression = QgsExpression( expString );
5429
5430 bool asc = values.value( 2 ).toBool();
5431
5432 QgsExpressionContext *unconstedContext = nullptr;
5433 QgsFeature f;
5434 if ( ctx )
5435 {
5436 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5437 // so no reason to worry
5438 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5439 f = ctx->feature();
5440 }
5441 else
5442 {
5443 // If there's no context provided, create a fake one
5444 unconstedContext = new QgsExpressionContext();
5445 }
5446
5447 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5448 Q_ASSERT( collection ); // Should have failed the multipart check above
5449
5451 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5452 QgsExpressionSorter sorter( orderBy );
5453
5454 QList<QgsFeature> partFeatures;
5455 partFeatures.reserve( collection->partCount() );
5456 for ( int i = 0; i < collection->partCount(); ++i )
5457 {
5458 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5459 partFeatures << f;
5460 }
5461
5462 sorter.sortFeatures( partFeatures, unconstedContext );
5463
5464 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5465
5466 Q_ASSERT( orderedGeom );
5467
5468 while ( orderedGeom->partCount() )
5469 orderedGeom->removeGeometry( 0 );
5470
5471 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5472 {
5473 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5474 }
5475
5476 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5477
5478 if ( !ctx )
5479 delete unconstedContext;
5480
5481 return result;
5482}
5483
5484static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5485{
5486 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5487 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5488
5489 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5490
5491 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5492 return result;
5493}
5494
5495static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5496{
5497 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5498 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5499
5500 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5501
5502 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5503 return result;
5504}
5505
5506static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5507{
5508 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5509 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5510
5511 QgsGeometry geom = lineGeom.interpolate( distance );
5512
5513 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5514 return result;
5515}
5516
5517static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5518{
5519 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5520 if ( lineGeom.type() != Qgis::GeometryType::Line )
5521 {
5522 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5523 return QVariant();
5524 }
5525
5526 const QgsCurve *curve = nullptr;
5527 if ( !lineGeom.isMultipart() )
5528 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5529 else
5530 {
5531 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5532 {
5533 if ( collection->numGeometries() > 0 )
5534 {
5535 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5536 }
5537 }
5538 }
5539 if ( !curve )
5540 return QVariant();
5541
5542 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5543 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5544
5545 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5546 QgsGeometry result( std::move( substring ) );
5547 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5548}
5549
5550static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5551{
5552 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5553 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5554
5555 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5556}
5557
5558static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5559{
5560 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5561 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5562 if ( vertex < 0 )
5563 {
5564 //negative idx
5565 int count = geom.constGet()->nCoordinates();
5566 vertex = count + vertex;
5567 }
5568
5569 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5570}
5571
5572static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5573{
5574 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5575 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5576 if ( vertex < 0 )
5577 {
5578 //negative idx
5579 int count = geom.constGet()->nCoordinates();
5580 vertex = count + vertex;
5581 }
5582
5583 return geom.distanceToVertex( vertex );
5584}
5585
5586static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5587{
5588 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5589 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5590
5591 double distance = lineGeom.lineLocatePoint( pointGeom );
5592
5593 return distance >= 0 ? distance : QVariant();
5594}
5595
5596static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5597{
5598 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5599 {
5600 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5601 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5602 }
5603
5604 if ( values.length() >= 1 )
5605 {
5606 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5607 return QVariant( qlonglong( std::round( number ) ) );
5608 }
5609
5610 return QVariant();
5611}
5612
5613static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5614{
5615 Q_UNUSED( values )
5616 Q_UNUSED( parent )
5617 return M_PI;
5618}
5619
5620static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5621{
5622 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5623 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5624 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5625 if ( places < 0 )
5626 {
5627 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5628 return QVariant();
5629 }
5630
5631 const bool omitGroupSeparator = values.value( 3 ).toBool();
5632 const bool trimTrailingZeros = values.value( 4 ).toBool();
5633
5634 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5635 if ( !omitGroupSeparator )
5636 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5637 else
5638 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5639
5640 QString res = locale.toString( value, 'f', places );
5641
5642 if ( trimTrailingZeros )
5643 {
5644#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5645 const QChar decimal = locale.decimalPoint();
5646 const QChar zeroDigit = locale.zeroDigit();
5647#else
5648 const QChar decimal = locale.decimalPoint().at( 0 );
5649 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5650#endif
5651
5652 if ( res.contains( decimal ) )
5653 {
5654 int trimPoint = res.length() - 1;
5655
5656 while ( res.at( trimPoint ) == zeroDigit )
5657 trimPoint--;
5658
5659 if ( res.at( trimPoint ) == decimal )
5660 trimPoint--;
5661
5662 res.truncate( trimPoint + 1 );
5663 }
5664 }
5665
5666 return res;
5667}
5668
5669static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5670{
5671 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5672 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5673 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5674
5675 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5676 return locale.toString( datetime, format );
5677}
5678
5679static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5680{
5681 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5682 int avg = ( color.red() + color.green() + color.blue() ) / 3;
5683 int alpha = color.alpha();
5684
5685 color.setRgb( avg, avg, avg, alpha );
5686
5687 return QgsSymbolLayerUtils::encodeColor( color );
5688}
5689
5690static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5691{
5692 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5693 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5694 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5695 if ( ratio > 1 )
5696 {
5697 ratio = 1;
5698 }
5699 else if ( ratio < 0 )
5700 {
5701 ratio = 0;
5702 }
5703
5704 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5705 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5706 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5707 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5708
5709 QColor newColor( red, green, blue, alpha );
5710
5711 return QgsSymbolLayerUtils::encodeColor( newColor );
5712}
5713
5714static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5715{
5716 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
5717 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5718 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5719 QColor color = QColor( red, green, blue );
5720 if ( ! color.isValid() )
5721 {
5722 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
5723 color = QColor( 0, 0, 0 );
5724 }
5725
5726 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5727}
5728
5729static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5730{
5731 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
5732 QVariant value = node->eval( parent, context );
5733 if ( parent->hasEvalError() )
5734 {
5735 parent->setEvalErrorString( QString() );
5736 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
5738 value = node->eval( parent, context );
5740 }
5741 return value;
5742}
5743
5744static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5745{
5746 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
5748 QVariant value = node->eval( parent, context );
5750 if ( value.toBool() )
5751 {
5752 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
5754 value = node->eval( parent, context );
5756 }
5757 else
5758 {
5759 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
5761 value = node->eval( parent, context );
5763 }
5764 return value;
5765}
5766
5767static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5768{
5769 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
5770 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5771 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5772 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
5773 QColor color = QColor( red, green, blue, alpha );
5774 if ( ! color.isValid() )
5775 {
5776 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
5777 color = QColor( 0, 0, 0 );
5778 }
5779 return QgsSymbolLayerUtils::encodeColor( color );
5780}
5781
5782QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5783{
5784 QgsGradientColorRamp expRamp;
5785 const QgsColorRamp *ramp = nullptr;
5786 if ( values.at( 0 ).userType() == QMetaType::type( "QgsGradientColorRamp" ) )
5787 {
5788 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
5789 ramp = &expRamp;
5790 }
5791 else
5792 {
5793 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5794 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
5795 if ( ! ramp )
5796 {
5797 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
5798 return QVariant();
5799 }
5800 }
5801
5802 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5803 QColor color = ramp->color( value );
5804 return QgsSymbolLayerUtils::encodeColor( color );
5805}
5806
5807static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5808{
5809 // Hue ranges from 0 - 360
5810 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5811 // Saturation ranges from 0 - 100
5812 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5813 // Lightness ranges from 0 - 100
5814 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5815
5816 QColor color = QColor::fromHslF( hue, saturation, lightness );
5817
5818 if ( ! color.isValid() )
5819 {
5820 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
5821 color = QColor( 0, 0, 0 );
5822 }
5823
5824 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5825}
5826
5827static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5828{
5829 // Hue ranges from 0 - 360
5830 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5831 // Saturation ranges from 0 - 100
5832 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5833 // Lightness ranges from 0 - 100
5834 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5835 // Alpha ranges from 0 - 255
5836 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
5837
5838 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
5839 if ( ! color.isValid() )
5840 {
5841 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
5842 color = QColor( 0, 0, 0 );
5843 }
5844 return QgsSymbolLayerUtils::encodeColor( color );
5845}
5846
5847static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5848{
5849 // Hue ranges from 0 - 360
5850 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5851 // Saturation ranges from 0 - 100
5852 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5853 // Value ranges from 0 - 100
5854 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5855
5856 QColor color = QColor::fromHsvF( hue, saturation, value );
5857
5858 if ( ! color.isValid() )
5859 {
5860 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
5861 color = QColor( 0, 0, 0 );
5862 }
5863
5864 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5865}
5866
5867static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5868{
5869 // Hue ranges from 0 - 360
5870 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
5871 // Saturation ranges from 0 - 100
5872 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5873 // Value ranges from 0 - 100
5874 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5875 // Alpha ranges from 0 - 255
5876 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
5877
5878 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
5879 if ( ! color.isValid() )
5880 {
5881 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
5882 color = QColor( 0, 0, 0 );
5883 }
5884 return QgsSymbolLayerUtils::encodeColor( color );
5885}
5886
5887static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5888{
5889 // Cyan ranges from 0 - 100
5890 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
5891 // Magenta ranges from 0 - 100
5892 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5893 // Yellow ranges from 0 - 100
5894 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5895 // Black ranges from 0 - 100
5896 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
5897
5898 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
5899
5900 if ( ! color.isValid() )
5901 {
5902 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
5903 color = QColor( 0, 0, 0 );
5904 }
5905
5906 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
5907}
5908
5909static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5910{
5911 // Cyan ranges from 0 - 100
5912 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
5913 // Magenta ranges from 0 - 100
5914 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
5915 // Yellow ranges from 0 - 100
5916 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
5917 // Black ranges from 0 - 100
5918 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
5919 // Alpha ranges from 0 - 255
5920 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
5921
5922 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
5923 if ( ! color.isValid() )
5924 {
5925 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
5926 color = QColor( 0, 0, 0 );
5927 }
5928 return QgsSymbolLayerUtils::encodeColor( color );
5929}
5930
5931static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5932{
5933 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5934 if ( ! color.isValid() )
5935 {
5936 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5937 return QVariant();
5938 }
5939
5940 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5941 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
5942 return color.red();
5943 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
5944 return color.green();
5945 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
5946 return color.blue();
5947 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
5948 return color.alpha();
5949 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
5950 return static_cast< double >( color.hsvHueF() * 360 );
5951 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
5952 return static_cast< double >( color.hsvSaturationF() * 100 );
5953 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
5954 return static_cast< double >( color.valueF() * 100 );
5955 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
5956 return static_cast< double >( color.hslHueF() * 360 );
5957 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
5958 return static_cast< double >( color.hslSaturationF() * 100 );
5959 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
5960 return static_cast< double >( color.lightnessF() * 100 );
5961 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
5962 return static_cast< double >( color.cyanF() * 100 );
5963 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
5964 return static_cast< double >( color.magentaF() * 100 );
5965 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
5966 return static_cast< double >( color.yellowF() * 100 );
5967 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
5968 return static_cast< double >( color.blackF() * 100 );
5969
5970 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
5971 return QVariant();
5972}
5973
5974static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5975{
5976 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
5977 if ( map.count() < 1 )
5978 {
5979 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
5980 return QVariant();
5981 }
5982
5983 QList< QColor > colors;
5985 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
5986 {
5987 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
5988 if ( !colors.last().isValid() )
5989 {
5990 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
5991 return QVariant();
5992 }
5993
5994 double step = it.key().toDouble();
5995 if ( it == map.constBegin() )
5996 {
5997 if ( step != 0.0 )
5998 stops << QgsGradientStop( step, colors.last() );
5999 }
6000 else if ( it == map.constEnd() )
6001 {
6002 if ( step != 1.0 )
6003 stops << QgsGradientStop( step, colors.last() );
6004 }
6005 else
6006 {
6007 stops << QgsGradientStop( step, colors.last() );
6008 }
6009 }
6010 bool discrete = values.at( 1 ).toBool();
6011
6012 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6013}
6014
6015static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6016{
6017 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6018 if ( ! color.isValid() )
6019 {
6020 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
6021 return QVariant();
6022 }
6023
6024 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6025 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6026 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6027 color.setRed( value );
6028 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6029 color.setGreen( value );
6030 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6031 color.setBlue( value );
6032 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6033 color.setAlpha( value );
6034 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6035 color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() );
6036 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6037 color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() );
6038 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6039 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() );
6040 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6041 color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() );
6042 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6043 color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() );
6044 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6045 color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() );
6046 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6047 color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6048 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6049 color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6050 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6051 color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() );
6052 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6053 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() );
6054 else
6055 {
6056 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6057 return QVariant();
6058 }
6059 return QgsSymbolLayerUtils::encodeColor( color );
6060}
6061
6062static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6063{
6064 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6065 if ( ! color.isValid() )
6066 {
6067 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
6068 return QVariant();
6069 }
6070
6071 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6072
6073 return QgsSymbolLayerUtils::encodeColor( color );
6074}
6075
6076static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6077{
6078 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6079 if ( ! color.isValid() )
6080 {
6081 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
6082 return QVariant();
6083 }
6084
6085 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6086
6087 return QgsSymbolLayerUtils::encodeColor( color );
6088}
6089
6090static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6091{
6092 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6093 QgsGeometry geom = feat.geometry();
6094 if ( !geom.isNull() )
6095 return QVariant::fromValue( geom );
6096 return QVariant();
6097}
6098
6099static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6100{
6101 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6102 if ( !feat.isValid() )
6103 return QVariant();
6104 return feat.id();
6105}
6106
6107static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6108{
6109 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6110 QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6111 QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6112
6114 if ( ! s.isValid() )
6115 return QVariant::fromValue( fGeom );
6117 if ( ! d.isValid() )
6118 return QVariant::fromValue( fGeom );
6119
6121 if ( context )
6122 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6123 QgsCoordinateTransform t( s, d, tContext );
6124 try
6125 {
6127 return QVariant::fromValue( fGeom );
6128 }
6129 catch ( QgsCsException &cse )
6130 {
6131 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6132 return QVariant();
6133 }
6134 return QVariant();
6135}
6136
6137
6138static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6139{
6140 bool foundLayer = false;
6141 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6142
6143 //no layer found
6144 if ( !featureSource || !foundLayer )
6145 {
6146 return QVariant();
6147 }
6148
6149 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6150
6152 req.setFilterFid( fid );
6153 req.setTimeout( 10000 );
6154 req.setRequestMayBeNested( true );
6155 if ( context )
6156 req.setFeedback( context->feedback() );
6157 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6158
6159 QgsFeature fet;
6160 QVariant result;
6161 if ( fIt.nextFeature( fet ) )
6162 result = QVariant::fromValue( fet );
6163
6164 return result;
6165}
6166
6167static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6168{
6169 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6170 bool foundLayer = false;
6171 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6172
6173 //no layer found
6174 if ( !featureSource || !foundLayer )
6175 {
6176 return QVariant();
6177 }
6179 QString cacheValueKey;
6180 if ( values.at( 1 ).type() == QVariant::Map )
6181 {
6182 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6183
6184 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6185 QString filterString;
6186 for ( ; i != attributeMap.constEnd(); ++i )
6187 {
6188 if ( !filterString.isEmpty() )
6189 {
6190 filterString.append( " AND " );
6191 }
6192 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6193 }
6194 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6195 if ( context && context->hasCachedValue( cacheValueKey ) )
6196 {
6197 return context->cachedValue( cacheValueKey );
6198 }
6199 req.setFilterExpression( filterString );
6200 }
6201 else
6202 {
6203 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6204 int attributeId = featureSource->fields().lookupField( attribute );
6205 if ( attributeId == -1 )
6206 {
6207 return QVariant();
6208 }
6209
6210 const QVariant &attVal = values.at( 2 );
6211
6212 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6213 if ( context && context->hasCachedValue( cacheValueKey ) )
6214 {
6215 return context->cachedValue( cacheValueKey );
6216 }
6217
6219 }
6220 req.setLimit( 1 );
6221 req.setTimeout( 10000 );
6222 req.setRequestMayBeNested( true );
6223 if ( context )
6224 req.setFeedback( context->feedback() );
6225 if ( !parent->needsGeometry() )
6226 {
6228 }
6229 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6230
6231 QgsFeature fet;
6232 QVariant res;
6233 if ( fIt.nextFeature( fet ) )
6234 {
6235 res = QVariant::fromValue( fet );
6236 }
6237
6238 if ( context )
6239 context->setCachedValue( cacheValueKey, res );
6240 return res;
6241}
6242
6243static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6244{
6245 QVariant result;
6246 QString fieldName;
6247
6248 if ( context )
6249 {
6250 if ( !values.isEmpty() )
6251 {
6252 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6253 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6254 fieldName = col->name();
6255 else if ( values.size() == 2 )
6256 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6257 }
6258
6259 QVariant value = values.at( 0 );
6260
6261 const QgsFields fields = context->fields();
6262 int fieldIndex = fields.lookupField( fieldName );
6263
6264 if ( fieldIndex == -1 )
6265 {
6266 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6267 }
6268 else
6269 {
6270 // TODO this function is NOT thread safe
6272 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6274
6275 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6276 if ( context->hasCachedValue( cacheValueKey ) )
6277 {
6278 return context->cachedValue( cacheValueKey );
6279 }
6280
6281 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6283
6284 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6285
6286 QVariant cache;
6287 if ( !context->hasCachedValue( cacheKey ) )
6288 {
6289 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6290 context->setCachedValue( cacheKey, cache );
6291 }
6292 else
6293 cache = context->cachedValue( cacheKey );
6294
6295 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6296
6297 context->setCachedValue( cacheValueKey, result );
6298 }
6299 }
6300 else
6301 {
6302 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6303 }
6304
6305 return result;
6306}
6307
6308static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6309{
6310 const QVariant data = values.at( 0 );
6311 const QMimeDatabase db;
6312 return db.mimeTypeForData( data.toByteArray() ).name();
6313}
6314
6315static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6316{
6317 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6318
6319 bool foundLayer = false;
6320 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6321 {
6322 if ( !layer )
6323 return QVariant();
6324
6325 // here, we always prefer the layer metadata values over the older server-specific published values
6326 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6327 return layer->name();
6328 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6329 return layer->id();
6330 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6331 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->title();
6332 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6333 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->abstract();
6334 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6335 {
6336 QStringList keywords;
6337 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6338 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6339 {
6340 keywords.append( it.value() );
6341 }
6342 if ( !keywords.isEmpty() )
6343 return keywords;
6344 return layer->keywordList();
6345 }
6346 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6347 return layer->dataUrl();
6348 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6349 {
6350 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->attribution() );
6351 }
6352 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6353 return layer->attributionUrl();
6354 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6355 return layer->publicSource();
6356 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6357 return layer->minimumScale();
6358 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6359 return layer->maximumScale();
6360 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6361 return layer->isEditable();
6362 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6363 return layer->crs().authid();
6364 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6365 return layer->crs().toProj();
6366 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6367 return layer->crs().description();
6368 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6369 {
6370 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6371 QVariant result = QVariant::fromValue( extentGeom );
6372 return result;
6373 }
6374 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6375 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6376 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6377 {
6378 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6379 return decodedUri.value( QStringLiteral( "path" ) );
6380 }
6381 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6382 {
6383 switch ( layer->type() )
6384 {
6385 case Qgis::LayerType::Vector:
6386 return QCoreApplication::translate( "expressions", "Vector" );
6387 case Qgis::LayerType::Raster:
6388 return QCoreApplication::translate( "expressions", "Raster" );
6389 case Qgis::LayerType::Mesh:
6390 return QCoreApplication::translate( "expressions", "Mesh" );
6391 case Qgis::LayerType::VectorTile:
6392 return QCoreApplication::translate( "expressions", "Vector Tile" );
6393 case Qgis::LayerType::Plugin:
6394 return QCoreApplication::translate( "expressions", "Plugin" );
6395 case Qgis::LayerType::Annotation:
6396 return QCoreApplication::translate( "expressions", "Annotation" );
6397 case Qgis::LayerType::PointCloud:
6398 return QCoreApplication::translate( "expressions", "Point Cloud" );
6399 case Qgis::LayerType::Group:
6400 return QCoreApplication::translate( "expressions", "Group" );
6401 }
6402 }
6403 else
6404 {
6405 //vector layer methods
6406 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6407 if ( vLayer )
6408 {
6409 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6410 return vLayer->storageType();
6411 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6413 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6414 return QVariant::fromValue( vLayer->featureCount() );
6415 }
6416 }
6417
6418 return QVariant();
6419 }, foundLayer );
6420
6421 if ( !foundLayer )
6422 return QVariant();
6423 else
6424 return res;
6425}
6426
6427static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6428{
6429 const QString uriPart = values.at( 1 ).toString();
6430
6431 bool foundLayer = false;
6432
6433 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
6434 {
6435 if ( !layer->dataProvider() )
6436 {
6437 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
6438 return QVariant();
6439 }
6440
6441 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
6442
6443 if ( !uriPart.isNull() )
6444 {
6445 return decodedUri.value( uriPart );
6446 }
6447 else
6448 {
6449 return decodedUri;
6450 }
6451 }, foundLayer );
6452
6453 if ( !foundLayer )
6454 {
6455 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
6456 return QVariant();
6457 }
6458 else
6459 {
6460 return res;
6461 }
6462}
6463
6464static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6465{
6466 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6467 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6468
6469 bool foundLayer = false;
6470 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
6471 {
6472 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6473 if ( !rl )
6474 return QVariant();
6475
6476 if ( band < 1 || band > rl->bandCount() )
6477 {
6478 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
6479 return QVariant();
6480 }
6481
6482 int stat = 0;
6483
6484 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6486 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6488 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6490 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6492 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6494 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6496 else
6497 {
6498 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6499 return QVariant();
6500 }
6501
6502 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6503 switch ( stat )
6504 {
6506 return stats.mean;
6508 return stats.stdDev;
6510 return stats.minimumValue;
6512 return stats.maximumValue;
6514 return stats.range;
6516 return stats.sum;
6517 }
6518 return QVariant();
6519 }, foundLayer );
6520
6521 if ( !foundLayer )
6522 {
6523#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
6524 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
6525#endif
6526 return QVariant();
6527 }
6528 else
6529 {
6530 return res;
6531 }
6532}
6533
6534static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6535{
6536 return values;
6537}
6538
6539static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6540{
6541 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6542 bool ascending = values.value( 1 ).toBool();
6543 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6544 return list;
6545}
6546
6547static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6548{
6549 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6550}
6551
6552static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6553{
6554 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6555}
6556
6557static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6558{
6559 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6560}
6561
6562static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6563{
6564 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6565 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6566 int match = 0;
6567 for ( const auto &item : listB )
6568 {
6569 if ( listA.contains( item ) )
6570 match++;
6571 }
6572
6573 return QVariant( match == listB.count() );
6574}
6575
6576static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6577{
6578 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
6579}
6580
6581static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6582{
6583 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6584 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6585 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
6586 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
6587 return list.at( list.length() + pos );
6588 return QVariant();
6589}
6590
6591static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6592{
6593 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6594 return list.value( 0 );
6595}
6596
6597static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6598{
6599 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6600 return list.value( list.size() - 1 );
6601}
6602
6603static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6604{
6605 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6606 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6607}
6608
6609static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6610{
6611 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6612 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6613}
6614
6615static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6616{
6617 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6618 int i = 0;
6619 double total = 0.0;
6620 for ( const QVariant &item : list )
6621 {
6622 switch ( item.userType() )
6623 {
6624 case QMetaType::Int:
6625 case QMetaType::UInt:
6626 case QMetaType::LongLong:
6627 case QMetaType::ULongLong:
6628 case QMetaType::Float:
6629 case QMetaType::Double:
6630 total += item.toDouble();
6631 ++i;
6632 break;
6633 }
6634 }
6635 return i == 0 ? QVariant() : total / i;
6636}
6637
6638static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6639{
6640 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6641 QVariantList numbers;
6642 for ( const auto &item : list )
6643 {
6644 switch ( item.userType() )
6645 {
6646 case QMetaType::Int:
6647 case QMetaType::UInt:
6648 case QMetaType::LongLong:
6649 case QMetaType::ULongLong:
6650 case QMetaType::Float:
6651 case QMetaType::Double:
6652 numbers.append( item );
6653 break;
6654 }
6655 }
6656 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6657 const int count = numbers.count();
6658 if ( count == 0 )
6659 {
6660 return QVariant();
6661 }
6662 else if ( count % 2 )
6663 {
6664 return numbers.at( count / 2 );
6665 }
6666 else
6667 {
6668 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
6669 }
6670}
6671
6672static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6673{
6674 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6675 int i = 0;
6676 double total = 0.0;
6677 for ( const QVariant &item : list )
6678 {
6679 switch ( item.userType() )
6680 {
6681 case QMetaType::Int:
6682 case QMetaType::UInt:
6683 case QMetaType::LongLong:
6684 case QMetaType::ULongLong:
6685 case QMetaType::Float:
6686 case QMetaType::Double:
6687 total += item.toDouble();
6688 ++i;
6689 break;
6690 }
6691 }
6692 return i == 0 ? QVariant() : total;
6693}
6694
6695static QVariant convertToSameType( const QVariant &value, QVariant::Type type )
6696{
6697 QVariant result = value;
6698 result.convert( static_cast<int>( type ) );
6699 return result;
6700}
6701
6702static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6703{
6704 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6705 QHash< QVariant, int > hash;
6706 for ( const auto &item : list )
6707 {
6708 ++hash[item];
6709 }
6710 const QList< int > occurrences = hash.values();
6711 if ( occurrences.empty() )
6712 return QVariantList();
6713
6714 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6715
6716 const QString option = values.at( 1 ).toString();
6717 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6718 {
6719 return convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() );
6720 }
6721 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6722 {
6723 if ( hash.isEmpty() )
6724 return QVariant();
6725
6726 return QVariant( hash.key( maxValue ) );
6727 }
6728 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6729 {
6730 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() ), context, parent, node );
6731 }
6732 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
6733 {
6734 if ( maxValue * 2 <= list.size() )
6735 return QVariant();
6736
6737 return QVariant( hash.key( maxValue ) );
6738 }
6739 else
6740 {
6741 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6742 return QVariant();
6743 }
6744}
6745
6746static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6747{
6748 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6749 QHash< QVariant, int > hash;
6750 for ( const auto &item : list )
6751 {
6752 ++hash[item];
6753 }
6754 const QList< int > occurrences = hash.values();
6755 if ( occurrences.empty() )
6756 return QVariantList();
6757
6758 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
6759
6760 const QString option = values.at( 1 ).toString();
6761 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6762 {
6763 return convertToSameType( hash.keys( minValue ), values.at( 0 ).type() );
6764 }
6765 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6766 {
6767 if ( hash.isEmpty() )
6768 return QVariant();
6769
6770 return QVariant( hash.key( minValue ) );
6771 }
6772 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6773 {
6774 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), values.at( 0 ).type() ), context, parent, node );
6775 }
6776 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
6777 {
6778 if ( hash.isEmpty() )
6779 return QVariant();
6780
6781 // Remove the majority, all others are minority
6782 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6783 if ( maxValue * 2 > list.size() )
6784 hash.remove( hash.key( maxValue ) );
6785
6786 return convertToSameType( hash.keys(), values.at( 0 ).type() );
6787 }
6788 else
6789 {
6790 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6791 return QVariant();
6792 }
6793}
6794
6795static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6796{
6797 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6798 list.append( values.at( 1 ) );
6799 return convertToSameType( list, values.at( 0 ).type() );
6800}
6801
6802static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6803{
6804 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6805 list.prepend( values.at( 1 ) );
6806 return convertToSameType( list, values.at( 0 ).type() );
6807}
6808
6809static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6810{
6811 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6812 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
6813 return convertToSameType( list, values.at( 0 ).type() );
6814}
6815
6816static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6817{
6818 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6819 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6820 if ( position < 0 )
6821 position = position + list.length();
6822 if ( position >= 0 && position < list.length() )
6823 list.removeAt( position );
6824 return convertToSameType( list, values.at( 0 ).type() );
6825}
6826
6827static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6828{
6829 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
6830 return QVariant();
6831
6832 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6833
6834 const QVariant toRemove = values.at( 1 );
6835 if ( QgsVariantUtils::isNull( toRemove ) )
6836 {
6837 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
6838 {
6839 return QgsVariantUtils::isNull( element );
6840 } ), list.end() );
6841 }
6842 else
6843 {
6844 list.removeAll( toRemove );
6845 }
6846 return convertToSameType( list, values.at( 0 ).type() );
6847}
6848
6849static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6850{
6851 if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
6852 {
6853 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6854
6855 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6856 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6857 {
6858 int index = list.indexOf( it.key() );
6859 while ( index >= 0 )
6860 {
6861 list.replace( index, it.value() );
6862 index = list.indexOf( it.key() );
6863 }
6864 }
6865
6866 return convertToSameType( list, values.at( 0 ).type() );
6867 }
6868 else if ( values.count() == 3 )
6869 {
6870 QVariantList before;
6871 QVariantList after;
6872 bool isSingleReplacement = false;
6873
6874 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).type() != QVariant::StringList )
6875 {
6876 before = QVariantList() << values.at( 1 );
6877 }
6878 else
6879 {
6880 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6881 }
6882
6883 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
6884 {
6885 after = QVariantList() << values.at( 2 );
6886 isSingleReplacement = true;
6887 }
6888 else
6889 {
6890 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
6891 }
6892
6893 if ( !isSingleReplacement && before.length() != after.length() )
6894 {
6895 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
6896 return QVariant();
6897 }
6898
6899 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6900 for ( int i = 0; i < before.length(); i++ )
6901 {
6902 int index = list.indexOf( before.at( i ) );
6903 while ( index >= 0 )
6904 {
6905 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
6906 index = list.indexOf( before.at( i ) );
6907 }
6908 }
6909
6910 return convertToSameType( list, values.at( 0 ).type() );
6911 }
6912 else
6913 {
6914 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
6915 return QVariant();
6916 }
6917}
6918
6919static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6920{
6921 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6922 QVariantList list_new;
6923
6924 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
6925 {
6926 while ( list.removeOne( cur ) )
6927 {
6928 list_new.append( cur );
6929 }
6930 }
6931
6932 list_new.append( list );
6933
6934 return convertToSameType( list_new, values.at( 0 ).type() );
6935}
6936
6937static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6938{
6939 QVariantList list;
6940 for ( const QVariant &cur : values )
6941 {
6942 list += QgsExpressionUtils::getListValue( cur, parent );
6943 }
6944 return convertToSameType( list, values.at( 0 ).type() );
6945}
6946
6947static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6948{
6949 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6950 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6951 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6952 int slice_length = 0;
6953 // negative positions means positions taken relative to the end of the array
6954 if ( start_pos < 0 )
6955 {
6956 start_pos = list.length() + start_pos;
6957 }
6958 if ( end_pos >= 0 )
6959 {
6960 slice_length = end_pos - start_pos + 1;
6961 }
6962 else
6963 {
6964 slice_length = list.length() + end_pos - start_pos + 1;
6965 }
6966 //avoid negative lengths in QList.mid function
6967 if ( slice_length < 0 )
6968 {
6969 slice_length = 0;
6970 }
6971 list = list.mid( start_pos, slice_length );
6972 return list;
6973}
6974
6975static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6976{
6977 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6978 std::reverse( list.begin(), list.end() );
6979 return list;
6980}
6981
6982static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6983{
6984 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6985 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6986 for ( const QVariant &cur : array2 )
6987 {
6988 if ( array1.contains( cur ) )
6989 return QVariant( true );
6990 }
6991 return QVariant( false );
6992}
6993
6994static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6995{
6996 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6997
6998 QVariantList distinct;
6999
7000 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7001 {
7002 if ( !distinct.contains( *it ) )
7003 {
7004 distinct += ( *it );
7005 }
7006 }
7007
7008 return distinct;
7009}
7010
7011static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7012{
7013 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7014 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7015 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7016
7017 QString str;
7018
7019 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7020 {
7021 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7022 if ( it != ( array.constEnd() - 1 ) )
7023 {
7024 str += delimiter;
7025 }
7026 }
7027
7028 return QVariant( str );
7029}
7030
7031static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7032{
7033 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7034 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7035 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7036
7037 QStringList list = str.split( delimiter );
7038 QVariantList array;
7039
7040 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7041 {
7042 array += ( !( *it ).isEmpty() ) ? *it : empty;
7043 }
7044
7045 return array;
7046}
7047
7048static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7049{
7050 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7051 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7052 if ( document.isNull() )
7053 return QVariant();
7054
7055 return document.toVariant();
7056}
7057
7058static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7059{
7060 Q_UNUSED( parent )
7061 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7062 return QString( document.toJson( QJsonDocument::Compact ) );
7063}
7064
7065static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7066{
7067 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7068 if ( str.isEmpty() )
7069 return QVariantMap();
7070 str = str.trimmed();
7071
7072 return QgsHstoreUtils::parse( str );
7073}
7074
7075static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7076{
7077 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7078 return QgsHstoreUtils::build( map );
7079}
7080
7081static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7082{
7083 QVariantMap result;
7084 for ( int i = 0; i + 1 < values.length(); i += 2 )
7085 {
7086 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7087 }
7088 return result;
7089}
7090
7091static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7092{
7093 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7094 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7095 QVariantMap resultMap;
7096
7097 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7098 {
7099 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7100 }
7101
7102 return resultMap;
7103}
7104
7105static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7106{
7107 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7108}
7109
7110static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7111{
7112 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7113}
7114
7115static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7116{
7117 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7118 map.remove( values.at( 1 ).toString() );
7119 return map;
7120}
7121
7122static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7123{
7124 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7125 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7126 return map;
7127}
7128
7129static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7130{
7131 QVariantMap result;
7132 for ( const QVariant &cur : values )
7133 {
7134 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7135 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7136 result.insert( it.key(), it.value() );
7137 }
7138 return result;
7139}
7140
7141static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7142{
7143 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7144}
7145
7146static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7147{
7148 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7149}
7150
7151static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7152{
7153 const QString envVarName = values.at( 0 ).toString();
7154 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7155 return QVariant();
7156
7157 return QProcessEnvironment::systemEnvironment().value( envVarName );
7158}
7159
7160static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7161{
7162 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7163 if ( parent->hasEvalError() )
7164 {
7165 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7166 return QVariant();
7167 }
7168 return QFileInfo( file ).completeBaseName();
7169}
7170
7171static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7172{
7173 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7174 if ( parent->hasEvalError() )
7175 {
7176 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7177 return QVariant();
7178 }
7179 return QFileInfo( file ).completeSuffix();
7180}
7181
7182static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7183{
7184 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7185 if ( parent->hasEvalError() )
7186 {
7187 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7188 return QVariant();
7189 }
7190 return QFileInfo::exists( file );
7191}
7192
7193static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7194{
7195 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7196 if ( parent->hasEvalError() )
7197 {
7198 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7199 return QVariant();
7200 }
7201 return QFileInfo( file ).fileName();
7202}
7203
7204static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7205{
7206 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7207 if ( parent->hasEvalError() )
7208 {
7209 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7210 return QVariant();
7211 }
7212 return QFileInfo( file ).isFile();
7213}
7214
7215static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7216{
7217 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7218 if ( parent->hasEvalError() )
7219 {
7220 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7221 return QVariant();
7222 }
7223 return QFileInfo( file ).isDir();
7224}
7225
7226static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7227{
7228 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7229 if ( parent->hasEvalError() )
7230 {
7231 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7232 return QVariant();
7233 }
7234 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7235}
7236
7237static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7238{
7239 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7240 if ( parent->hasEvalError() )
7241 {
7242 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7243 return QVariant();
7244 }
7245 return QFileInfo( file ).size();
7246}
7247
7248static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7249{
7250 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7251}
7252
7253static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7254{
7255 QVariant hash;
7256 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7257 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7258
7259 if ( method == QLatin1String( "md4" ) )
7260 {
7261 hash = fcnHash( str, QCryptographicHash::Md4 );
7262 }
7263 else if ( method == QLatin1String( "md5" ) )
7264 {
7265 hash = fcnHash( str, QCryptographicHash::Md5 );
7266 }
7267 else if ( method == QLatin1String( "sha1" ) )
7268 {
7269 hash = fcnHash( str, QCryptographicHash::Sha1 );
7270 }
7271 else if ( method == QLatin1String( "sha224" ) )
7272 {
7273 hash = fcnHash( str, QCryptographicHash::Sha224 );
7274 }
7275 else if ( method == QLatin1String( "sha256" ) )
7276 {
7277 hash = fcnHash( str, QCryptographicHash::Sha256 );
7278 }
7279 else if ( method == QLatin1String( "sha384" ) )
7280 {
7281 hash = fcnHash( str, QCryptographicHash::Sha384 );
7282 }
7283 else if ( method == QLatin1String( "sha512" ) )
7284 {
7285 hash = fcnHash( str, QCryptographicHash::Sha512 );
7286 }
7287 else if ( method == QLatin1String( "sha3_224" ) )
7288 {
7289 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7290 }
7291 else if ( method == QLatin1String( "sha3_256" ) )
7292 {
7293 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7294 }
7295 else if ( method == QLatin1String( "sha3_384" ) )
7296 {
7297 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7298 }
7299 else if ( method == QLatin1String( "sha3_512" ) )
7300 {
7301 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7302 }
7303 else if ( method == QLatin1String( "keccak_224" ) )
7304 {
7305 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7306 }
7307 else if ( method == QLatin1String( "keccak_256" ) )
7308 {
7309 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7310 }
7311 else if ( method == QLatin1String( "keccak_384" ) )
7312 {
7313 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7314 }
7315 else if ( method == QLatin1String( "keccak_512" ) )
7316 {
7317 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7318 }
7319 else
7320 {
7321 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7322 }
7323 return hash;
7324}
7325
7326static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7327{
7328 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7329}
7330
7331static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7332{
7333 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7334}
7335
7336static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7337{
7338 const QByteArray input = values.at( 0 ).toByteArray();
7339 return QVariant( QString( input.toBase64() ) );
7340}
7341
7342static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7343{
7344 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7345 QUrlQuery query;
7346 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7347 {
7348 query.addQueryItem( it.key(), it.value().toString() );
7349 }
7350 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7351}
7352
7353static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7354{
7355 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7356 const QByteArray base64 = value.toLocal8Bit();
7357 const QByteArray decoded = QByteArray::fromBase64( base64 );
7358 return QVariant( decoded );
7359}
7360
7361typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7362
7363static 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 )
7364{
7365
7366 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7367 // TODO this function is NOT thread safe
7369 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7371
7372 QgsFeatureRequest request;
7373 request.setTimeout( 10000 );
7374 request.setRequestMayBeNested( true );
7375 request.setFeedback( context->feedback() );
7376
7377 // First parameter is the overlay layer
7378 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7380
7381 const bool layerCanBeCached = node->isStatic( parent, context );
7382 QVariant targetLayerValue = node->eval( parent, context );
7384
7385 // Second parameter is the expression to evaluate (or null for testonly)
7386 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7388 QString subExpString = node->dump();
7389
7390 bool testOnly = ( subExpString == "NULL" );
7391 // TODO this function is NOT thread safe
7393 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7395 if ( !targetLayer ) // No layer, no joy
7396 {
7397 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7398 return QVariant();
7399 }
7400
7401 // Third parameter is the filtering expression
7402 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7404 QString filterString = node->dump();
7405 if ( filterString != "NULL" )
7406 {
7407 request.setFilterExpression( filterString ); //filter cached features
7408 }
7409
7410 // Fourth parameter is the limit
7411 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7413 QVariant limitValue = node->eval( parent, context );
7415 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7416
7417 // Fifth parameter (for nearest only) is the max distance
7418 double max_distance = 0;
7419 if ( isNearestFunc ) //maxdistance param handling
7420 {
7421 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7423 QVariant distanceValue = node->eval( parent, context );
7425 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
7426 }
7427
7428 // Fifth or sixth (for nearest only) parameter is the cache toggle
7429 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
7431 QVariant cacheValue = node->eval( parent, context );
7433 bool cacheEnabled = cacheValue.toBool();
7434
7435 // Sixth parameter (for intersects only) is the min overlap (area or length)
7436 // Seventh parameter (for intersects only) is the min inscribed circle radius
7437 // Eighth parameter (for intersects only) is the return_details
7438 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
7439 double minOverlap { -1 };
7440 double minInscribedCircleRadius { -1 };
7441 bool returnDetails = false; //#spellok
7442 bool sortByMeasure = false;
7443 bool sortAscending = false;
7444 bool requireMeasures = false;
7445 bool overlapOrRadiusFilter = false;
7446 if ( isIntersectsFunc )
7447 {
7448
7449 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7451 const QVariant minOverlapValue = node->eval( parent, context );
7453 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
7454 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7456 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
7458 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
7459 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
7460 // Return measures is only effective when an expression is set
7461 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
7462 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
7463 // Sort by measures is only effective when an expression is set
7464 const QString sorting { node->eval( parent, context ).toString().toLower() };
7465 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
7466 sortAscending = sorting.startsWith( "asc" );
7467 requireMeasures = sortByMeasure || returnDetails; //#spellok
7468 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
7469 }
7470
7471
7472 FEAT_FROM_CONTEXT( context, feat )
7473 const QgsGeometry geometry = feat.geometry();
7474
7475 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7476 {
7477 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7478 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7479 }
7480
7481 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7482
7483 QgsRectangle intDomain = geometry.boundingBox();
7484 if ( bboxGrow != 0 )
7485 {
7486 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7487 }
7488
7489 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7490
7491 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7492 // Otherwise, it can be toggled by the user
7493 QgsSpatialIndex spatialIndex;
7494 QgsVectorLayer *cachedTarget;
7495 QList<QgsFeature> features;
7496 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7497 {
7498 // If the cache (local spatial index) is enabled, we materialize the whole
7499 // layer, then do the request on that layer instead.
7500 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7501 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7502
7503 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7504 {
7505 cachedTarget = targetLayer->materialize( request );
7506 if ( layerCanBeCached )
7507 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7508 }
7509 else
7510 {
7511 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7512 }
7513
7514 if ( !context->hasCachedValue( cacheIndex ) )
7515 {
7516 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7517 if ( layerCanBeCached )
7518 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7519 }
7520 else
7521 {
7522 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7523 }
7524
7525 QList<QgsFeatureId> fidsList;
7526 if ( isNearestFunc )
7527 {
7528 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7529 }
7530 else
7531 {
7532 fidsList = spatialIndex.intersects( intDomain );
7533 }
7534
7535 QListIterator<QgsFeatureId> i( fidsList );
7536 while ( i.hasNext() )
7537 {
7538 QgsFeatureId fId2 = i.next();
7539 if ( sameLayers && feat.id() == fId2 )
7540 continue;
7541 features.append( cachedTarget->getFeature( fId2 ) );
7542 }
7543
7544 }
7545 else
7546 {
7547 // If the cache (local spatial index) is not enabled, we directly
7548 // get the features from the target layer
7549 request.setFilterRect( intDomain );
7550 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7551 QgsFeature feat2;
7552 while ( fit.nextFeature( feat2 ) )
7553 {
7554 if ( sameLayers && feat.id() == feat2.id() )
7555 continue;
7556 features.append( feat2 );
7557 }
7558 }
7559
7560 QgsExpression subExpression;
7561 QgsExpressionContext subContext;
7562 if ( !testOnly )
7563 {
7564 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7565 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7566
7567 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7568 {
7569 subExpression = QgsExpression( subExpString );
7571 subExpression.prepare( &subContext );
7572 }
7573 else
7574 {
7575 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
7576 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
7577 }
7578 }
7579
7580 // //////////////////////////////////////////////////////////////////
7581 // Helper functions for geometry tests
7582
7583 // Test function for linestring geometries, returns TRUE if test passes
7584 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
7585 {
7586 bool testResult { false };
7587 // For return measures:
7588 QVector<double> overlapValues;
7589 for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
7590 {
7591 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
7592 // Check min overlap for intersection (if set)
7593 if ( minOverlap != -1 || requireMeasures )
7594 {
7595 overlapValue = geom->length();
7596 overlapValues.append( overlapValue );
7597 if ( minOverlap != -1 )
7598 {
7599 if ( overlapValue >= minOverlap )
7600 {
7601 testResult = true;
7602 }
7603 else
7604 {
7605 continue;
7606 }
7607 }
7608 }
7609 }
7610
7611 if ( ! overlapValues.isEmpty() )
7612 {
7613 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7614 }
7615
7616 return testResult;
7617 };
7618
7619 // Test function for polygon geometries, returns TRUE if test passes
7620 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
7621 {
7622 // overlap and inscribed circle tests must be checked both (if the values are != -1)
7623 bool testResult { false };
7624 // For return measures:
7625 QVector<double> overlapValues;
7626 QVector<double> radiusValues;
7627 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
7628 {
7629 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
7630 // Check min overlap for intersection (if set)
7631 if ( minOverlap != -1 || requireMeasures )
7632 {
7633 overlapValue = geom->area();
7634 overlapValues.append( geom->area() );
7635 if ( minOverlap != - 1 )
7636 {
7637 if ( overlapValue >= minOverlap )
7638 {
7639 testResult = true;
7640 }
7641 else
7642 {
7643 continue;
7644 }
7645 }
7646 }
7647
7648 // Check min inscribed circle radius for intersection (if set)
7649 if ( minInscribedCircleRadius != -1 || requireMeasures )
7650 {
7651 const QgsRectangle bbox = geom->boundingBox();
7652 const double width = bbox.width();
7653 const double height = bbox.height();
7654 const double size = width > height ? width : height;
7655 const double tolerance = size / 100.0;
7656 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
7657 testResult = radiusValue >= minInscribedCircleRadius;
7658 radiusValues.append( radiusValues );
7659 }
7660 } // end for parts
7661
7662 // Get the max values
7663 if ( !radiusValues.isEmpty() )
7664 {
7665 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
7666 }
7667
7668 if ( ! overlapValues.isEmpty() )
7669 {
7670 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7671 }
7672
7673 return testResult;
7674
7675 };
7676
7677
7678 bool found = false;
7679 int foundCount = 0;
7680 QVariantList results;
7681
7682 QListIterator<QgsFeature> i( features );
7683 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
7684 {
7685
7686 QgsFeature feat2 = i.next();
7687
7688
7689 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
7690 {
7691
7692 double overlapValue = -1;
7693 double radiusValue = -1;
7694
7695 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
7696 {
7697 const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
7698
7699 // Depending on the intersection geometry type and on the geometry type of
7700 // the tested geometry we can run different tests and collect different measures
7701 // that can be used for sorting (if required).
7702 switch ( intersection.type() )
7703 {
7704
7705 case Qgis::GeometryType::Polygon:
7706 {
7707
7708 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
7709 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
7710
7711 if ( ! testResult && overlapOrRadiusFilter )
7712 {
7713 continue;
7714 }
7715
7716 break;
7717 }
7718
7719 case Qgis::GeometryType::Line:
7720 {
7721
7722 // If the intersection is a linestring and a minimum circle is required
7723 // we can discard this result immediately.
7724 if ( minInscribedCircleRadius != -1 )
7725 {
7726 continue;
7727 }
7728
7729 // Otherwise a test for the overlap value is performed.
7730 const bool testResult { testLinestring( intersection, overlapValue ) };
7731
7732 if ( ! testResult && overlapOrRadiusFilter )
7733 {
7734 continue;
7735 }
7736
7737 break;
7738 }
7739
7740 case Qgis::GeometryType::Point:
7741 {
7742
7743 // If the intersection is a point and a minimum circle is required
7744 // we can discard this result immediately.
7745 if ( minInscribedCircleRadius != -1 )
7746 {
7747 continue;
7748 }
7749
7750 bool testResult { false };
7751 if ( minOverlap != -1 || requireMeasures )
7752 {
7753 // Initially set this to 0 because it's a point intersection...
7754 overlapValue = 0;
7755 // ... but if the target geometry is not a point and the source
7756 // geometry is a point, we must record the length or the area
7757 // of the intersected geometry and use that as a measure for
7758 // sorting or reporting.
7759 if ( geometry.type() == Qgis::GeometryType::Point )
7760 {
7761 switch ( feat2.geometry().type() )
7762 {
7763 case Qgis::GeometryType::Unknown:
7764 case Qgis::GeometryType::Null:
7765 case Qgis::GeometryType::Point:
7766 {
7767 break;
7768 }
7769 case Qgis::GeometryType::Line:
7770 {
7771 testResult = testLinestring( feat2.geometry(), overlapValue );
7772 break;
7773 }
7774 case Qgis::GeometryType::Polygon:
7775 {
7776 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
7777 break;
7778 }
7779 }
7780 }
7781
7782 if ( ! testResult && overlapOrRadiusFilter )
7783 {
7784 continue;
7785 }
7786
7787 }
7788 break;
7789 }
7790
7791 case Qgis::GeometryType::Null:
7792 case Qgis::GeometryType::Unknown:
7793 {
7794 continue;
7795 }
7796 }
7797 }
7798
7799 found = true;
7800 foundCount++;
7801
7802 // We just want a single boolean result if there is any intersect: finish and return true
7803 if ( testOnly )
7804 break;
7805
7806 if ( !invert )
7807 {
7808 // We want a list of attributes / geometries / other expression values, evaluate now
7809 subContext.setFeature( feat2 );
7810 const QVariant expResult = subExpression.evaluate( &subContext );
7811
7812 if ( requireMeasures )
7813 {
7814 QVariantMap resultRecord;
7815 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
7816 resultRecord.insert( QStringLiteral( "result" ), expResult );
7817 // Overlap is always added because return measures was set
7818 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
7819 // Radius is only added when is different than -1 (because for linestrings is not set)
7820 if ( radiusValue != -1 )
7821 {
7822 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
7823 }
7824 results.append( resultRecord );
7825 }
7826 else
7827 {
7828 results.append( expResult );
7829 }
7830 }
7831 else
7832 {
7833 // If not, results is a list of found ids, which we'll inverse and evaluate below
7834 results.append( feat2.id() );
7835 }
7836 }
7837 }
7838
7839 if ( testOnly )
7840 {
7841 if ( invert )
7842 found = !found;//for disjoint condition
7843 return found;
7844 }
7845
7846 if ( !invert )
7847 {
7848 if ( requireMeasures )
7849 {
7850 if ( sortByMeasure )
7851 {
7852 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
7853 {
7854 return sortAscending ?
7855 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
7856 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
7857 } );
7858 }
7859 // Resize
7860 if ( limit > 0 && results.size() > limit )
7861 {
7862 results.erase( results.begin() + limit );
7863 }
7864
7865 if ( ! returnDetails ) //#spellok
7866 {
7867 QVariantList expResults;
7868 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
7869 {
7870 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
7871 }
7872 return expResults;
7873 }
7874 }
7875
7876 return results;
7877 }
7878
7879 // for disjoint condition returns the results for cached layers not intersected feats
7880 QVariantList disjoint_results;
7881 QgsFeature feat2;
7882 QgsFeatureRequest request2;
7883 request2.setLimit( limit );
7884 if ( context )
7885 request2.setFeedback( context->feedback() );
7886 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
7887 while ( fi.nextFeature( feat2 ) )
7888 {
7889 if ( !results.contains( feat2.id() ) )
7890 {
7891 subContext.setFeature( feat2 );
7892 disjoint_results.append( subExpression.evaluate( &subContext ) );
7893 }
7894 }
7895 return disjoint_results;
7896
7897}
7898
7899// Intersect functions:
7900
7901static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7902{
7903 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
7904}
7905
7906static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7907{
7908 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
7909}
7910
7911static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7912{
7913 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
7914}
7915
7916static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7917{
7918 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
7919}
7920
7921static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7922{
7923 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
7924}
7925
7926static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7927{
7928 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
7929}
7931static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7932{
7933 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
7934}
7935
7936static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7937{
7938 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
7939}
7940
7941const QList<QgsExpressionFunction *> &QgsExpression::Functions()
7942{
7943 // The construction of the list isn't thread-safe, and without the mutex,
7944 // crashes in the WFS provider may occur, since it can parse expressions
7945 // in parallel.
7946 // The mutex needs to be recursive.
7947 static QRecursiveMutex sFunctionsMutex;
7948 QMutexLocker locker( &sFunctionsMutex );
7949
7950 QList<QgsExpressionFunction *> &functions = *sFunctions();
7951
7952 if ( functions.isEmpty() )
7953 {
7955 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
7956 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
7957 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
7958
7959 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
7960 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7961 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7962
7963 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
7964 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7965
7966 functions
7967 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
7968 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
7969 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
7970 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
7971 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
7972 << 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" ) )
7973 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
7974 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
7975 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
7976 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
7977 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
7978 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
7979 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
7980 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
7981 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
7982 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
7983 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
7984 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
7985 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
7986
7987 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
7988 randFunc->setIsStatic( false );
7989 functions << randFunc;
7990
7991 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" ) );
7992 randfFunc->setIsStatic( false );
7993 functions << randfFunc;
7994
7995 functions
7996 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7997 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7998 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
7999 << 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" ) )
8000 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exp" ), 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" ) ), fcnExpScale, QStringLiteral( "Math" ) )
8001 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8002 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8003 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8004 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8005 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8006 << 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" ) )
8007 << 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" ) )
8008 << 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" ) )
8009 << 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" ) )
8010 << 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" ) )
8011 << 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" ) )
8012 << 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" ) )
8013 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8014 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8015 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8016 << 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 )
8017 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8018
8019 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8021 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8022 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8023 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8024 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8025 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8026 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8027 fcnAggregate,
8028 QStringLiteral( "Aggregates" ),
8029 QString(),
8030 []( const QgsExpressionNodeFunction * node )
8031 {
8032 // usesGeometry callback: return true if @parent variable is referenced
8033
8034 if ( !node )
8035 return true;
8036
8037 if ( !node->args() )
8038 return false;
8039
8040 QSet<QString> referencedVars;
8041 if ( node->args()->count() > 2 )
8042 {
8043 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8044 referencedVars = subExpressionNode->referencedVariables();
8045 }
8046
8047 if ( node->args()->count() > 3 )
8048 {
8049 QgsExpressionNode *filterNode = node->args()->at( 3 );
8050 referencedVars.unite( filterNode->referencedVariables() );
8051 }
8052 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8053 },
8054 []( const QgsExpressionNodeFunction * node )
8055 {
8056 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8057
8058 if ( !node )
8059 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8060
8061 if ( !node->args() )
8062 return QSet<QString>();
8063
8064 QSet<QString> referencedCols;
8065 QSet<QString> referencedVars;
8066
8067 if ( node->args()->count() > 2 )
8068 {
8069 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8070 referencedVars = subExpressionNode->referencedVariables();
8071 referencedCols = subExpressionNode->referencedColumns();
8072 }
8073 if ( node->args()->count() > 3 )
8074 {
8075 QgsExpressionNode *filterNode = node->args()->at( 3 );
8076 referencedVars = filterNode->referencedVariables();
8077 referencedCols.unite( filterNode->referencedColumns() );
8078 }
8079
8080 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8081 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8082 else
8083 return referencedCols;
8084 },
8085 true
8086 )
8087
8088 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8089 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8090 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8091 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8092 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8093 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8094 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8095
8096 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8097 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8098 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8099 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8100 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8101 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8102 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8103 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8104 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8105 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8106 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8107 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8108 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8109 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8110 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8111 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8112 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8113 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8114 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8115 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8116 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8117
8118 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8119 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8120
8121 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8122 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8123 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8124 fcnAge, QStringLiteral( "Date and Time" ) )
8125 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8126 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8127 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8128 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8129 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8130 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8131 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8132 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8133 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8134 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8135 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8136 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8137 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8138 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8139 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8140 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8141 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8142 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8143 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8144 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8145 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8146 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8147 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8148 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8149 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8150 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8151 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8152 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8153 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8154 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8155 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8156 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8157 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8158 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8159 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8160 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8161 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8162 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8163 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8164 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8165 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8166 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8167 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8168 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8169 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8170 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8171 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8172 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8173 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8174 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8175 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8176 false, QSet< QString >(), false, QStringList(), true )
8177 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8178 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8179 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8180 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8181 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8182 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8183 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8184 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8185 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8186 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8187 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8188 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8189 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8190 << 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" ) )
8191 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8192 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8193 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8194 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8195 fcnColorMixRgb, QStringLiteral( "Color" ) )
8196 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8197 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8198 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8199 fcnColorRgb, QStringLiteral( "Color" ) )
8200 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8201 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8202 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8203 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8204 fncColorRgba, QStringLiteral( "Color" ) )
8205 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8206 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8207 fcnRampColor, QStringLiteral( "Color" ) )
8208 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8209 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8210 fcnCreateRamp, QStringLiteral( "Color" ) )
8211 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8212 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8213 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8214 fcnColorHsl, QStringLiteral( "Color" ) )
8215 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8216 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8217 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8218 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8219 fncColorHsla, QStringLiteral( "Color" ) )
8220 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8221 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8222 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8223 fcnColorHsv, QStringLiteral( "Color" ) )
8224 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8225 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8226 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8227 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8228 fncColorHsva, QStringLiteral( "Color" ) )
8229 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8230 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8231 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8232 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8233 fcnColorCmyk, QStringLiteral( "Color" ) )
8234 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8235 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8236 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8237 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8238 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8239 fncColorCmyka, QStringLiteral( "Color" ) )
8240 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8241 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8242 fncColorPart, QStringLiteral( "Color" ) )
8243 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8244 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8245 fncDarker, QStringLiteral( "Color" ) )
8246 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8247 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8248 fncLighter, QStringLiteral( "Color" ) )
8249 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8250
8251 // file info
8252 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8253 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8254 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8255 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8256 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8257 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8258 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8259 fcnFileName, QStringLiteral( "Files and Paths" ) )
8260 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8261 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8262 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8263 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8264 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8265 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8266 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8267 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8268
8269 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8270 fcnExif, QStringLiteral( "Files and Paths" ) )
8271 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8272 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8273
8274 // hash
8275 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8276 fcnGenericHash, QStringLiteral( "Conversions" ) )
8277 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8278 fcnHashMd5, QStringLiteral( "Conversions" ) )
8279 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8280 fcnHashSha256, QStringLiteral( "Conversions" ) )
8281
8282 //base64
8283 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8284 fcnToBase64, QStringLiteral( "Conversions" ) )
8285 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8286 fcnFromBase64, QStringLiteral( "Conversions" ) )
8287
8288 // deprecated stuff - hidden from users
8289 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8290
8291 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8292 geomFunc->setIsStatic( false );
8293 functions << geomFunc;
8294
8295 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8296 areaFunc->setIsStatic( false );
8297 functions << areaFunc;
8298
8299 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8300
8301 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8302 lengthFunc->setIsStatic( false );
8303 functions << lengthFunc;
8304
8305 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8306 perimeterFunc->setIsStatic( false );
8307 functions << perimeterFunc;
8308
8309 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8310
8311 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8313 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
8314
8315 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
8316 xFunc->setIsStatic( false );
8317 functions << xFunc;
8318
8319 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
8320 yFunc->setIsStatic( false );
8321 functions << yFunc;
8322
8323 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
8324 zFunc->setIsStatic( false );
8325 functions << zFunc;
8326
8327 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
8328 {
8329 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
8330 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
8331 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
8332 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
8333 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
8334 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
8335 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
8336 };
8337 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
8338 while ( i.hasNext() )
8339 {
8340 i.next();
8342 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8343 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8344 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8345 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
8346 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
8347 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
8348 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
8349 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
8350 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
8351 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8352
8353 // The current feature is accessed for the geometry, so this should not be cached
8354 fcnGeomOverlayFunc->setIsStatic( false );
8355 functions << fcnGeomOverlayFunc;
8356 }
8357
8358 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
8359 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8360 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8361 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8362 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
8363 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
8364 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
8365 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8366 // The current feature is accessed for the geometry, so this should not be cached
8367 fcnGeomOverlayNearestFunc->setIsStatic( false );
8368 functions << fcnGeomOverlayNearestFunc;
8369
8370 functions
8371 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
8372 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
8373 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
8374 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
8375 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
8376 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
8377 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
8378 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
8379 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8380 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
8381 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
8382 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
8383 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
8384 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
8385 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8386 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
8387 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
8388 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
8389 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
8390 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
8391 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8392 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8393 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
8394 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
8395 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
8396 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8397 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8398 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8399 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
8400 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
8401 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8402 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
8403 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
8404 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8405 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8406 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
8407 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
8408 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8409 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8410 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
8411 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
8412 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
8413 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
8414 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8415 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
8416 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
8417 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
8418 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8419 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8420 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
8421 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
8422 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
8423 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
8424 {
8425 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8426#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
8427 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
8428#else
8429 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
8430#endif
8431 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
8432 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
8433
8434 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
8435 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
8436 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
8437 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
8438
8439 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
8440 xAtFunc->setIsStatic( false );
8441 functions << xAtFunc;
8442
8443
8444 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
8445 yAtFunc->setIsStatic( false );
8446 functions << yAtFunc;
8447
8448 functions
8449 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
8450 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
8451 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
8452 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
8453 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
8454 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
8455 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8456 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
8457 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
8458 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
8459 << 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" ) )
8460 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8461 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8462 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
8463 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8464 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8465 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
8466 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8467 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8468 fcnTouches, QStringLiteral( "GeometryGroup" ) )
8469 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8470 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8471 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
8472 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8473 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8474 fcnContains, QStringLiteral( "GeometryGroup" ) )
8475 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8476 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8477 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
8478 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8479 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8480 fcnWithin, QStringLiteral( "GeometryGroup" ) )
8481 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8482 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
8483 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
8484 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
8485 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8486 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
8487 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
8488 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
8489 fcnRotate, QStringLiteral( "GeometryGroup" ) )
8490 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8491 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
8492 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
8493 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
8494 fcnScale, QStringLiteral( "GeometryGroup" ) )
8495 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8496 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
8497 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
8498 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
8499 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
8500 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
8501 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
8502 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
8503 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
8504 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
8505 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
8506 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8507 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8508 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
8509 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
8510 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
8511 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
8512 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
8513 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8514 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
8515 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8516 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
8517 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8518 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
8519 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8520 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8521 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
8522 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
8523 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
8524 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8525 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
8526 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
8527 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8528 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
8529 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8530 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8531 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
8532 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8533 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8534 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8535 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8536 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8537 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
8538 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8539 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8540 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8541 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8542 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8543 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
8544 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8545 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
8546 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
8547 fcnExtend, QStringLiteral( "GeometryGroup" ) )
8548 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
8549 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
8550 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8551 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
8552 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
8553 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
8554 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8555 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8556 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
8557 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8558 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8559 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
8560 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
8561 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
8562 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
8563 {
8564 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
8565 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8566 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
8567 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
8568 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
8569 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
8570 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
8571 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
8572 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
8573 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
8574 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
8575 {
8576 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8577 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8578 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8579 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8580 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
8581 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
8582 {
8583 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8584 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8585 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8586 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8587 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8588 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8589 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8590 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
8591 {
8592 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8593 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8594 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8595 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8596 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
8597 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
8598 {
8599 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8600 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8601 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8602 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8603 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8604 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8605 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8606 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
8607 {
8608 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8609 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8610 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8611 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8612 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
8613 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
8614 {
8615 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8616 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8617 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8618 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8619 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8620 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8621 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8622 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
8623 {
8624 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8625 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
8626 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
8627 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
8628 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
8629 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
8630 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
8631 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
8632 {
8633 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8634 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
8635 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
8636 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
8637 {
8638 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8639 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8640 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
8641 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
8642 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
8643 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
8644 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
8645 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
8646 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
8647 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
8648 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
8649 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
8650 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8651 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
8652#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
8653 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8654 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
8655 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
8656#endif
8657 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
8658 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8659 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
8660 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
8661 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8662 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
8663 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
8664 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8665 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8666 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
8667 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8668 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8669 fcnDifference, QStringLiteral( "GeometryGroup" ) )
8670 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8671 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8672 fcnDistance, QStringLiteral( "GeometryGroup" ) )
8673 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8674 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
8675 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
8676 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8677 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8678 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
8679 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8680 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8681 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
8682 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8683 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8684 fcnCombine, QStringLiteral( "GeometryGroup" ) )
8685 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8686 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8687 fcnCombine, QStringLiteral( "GeometryGroup" ) )
8688 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8689 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
8690 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
8691 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8692 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8693 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
8694 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8695 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
8696 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
8697 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
8698 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8699 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8700 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
8701 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
8702 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8703 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
8704 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8705 fcnZMax, QStringLiteral( "GeometryGroup" ) )
8706 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8707 fcnZMin, QStringLiteral( "GeometryGroup" ) )
8708 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8709 fcnMMax, QStringLiteral( "GeometryGroup" ) )
8710 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8711 fcnMMin, QStringLiteral( "GeometryGroup" ) )
8712 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8713 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
8714 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8715 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
8716
8717
8718 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8719 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
8720 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
8721 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
8722
8723 orderPartsFunc->setIsStaticFunction(
8724 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8725 {
8726 const QList< QgsExpressionNode *> argList = node->args()->list();
8727 for ( QgsExpressionNode *argNode : argList )
8728 {
8729 if ( !argNode->isStatic( parent, context ) )
8730 return false;
8731 }
8732
8733 if ( node->args()->count() > 1 )
8734 {
8735 QgsExpressionNode *argNode = node->args()->at( 1 );
8736
8737 QString expString = argNode->eval( parent, context ).toString();
8738
8739 QgsExpression e( expString );
8740
8741 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
8742 return true;
8743 }
8744
8745 return true;
8746 } );
8747
8748 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8749 {
8750 if ( node->args()->count() > 1 )
8751 {
8752 QgsExpressionNode *argNode = node->args()->at( 1 );
8753 QString expression = argNode->eval( parent, context ).toString();
8754 QgsExpression e( expression );
8755 e.prepare( context );
8756 context->setCachedValue( expression, QVariant::fromValue( e ) );
8757 }
8758 return true;
8759 }
8760 );
8761 functions << orderPartsFunc;
8762
8763 functions
8764 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8765 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8766 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
8767 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8768 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8769 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
8770 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8771 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
8772 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8773 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
8774 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8775 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
8776 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8777 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
8778 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8779 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
8780 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8781 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
8782
8783
8784 // **Record** functions
8785
8786 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
8787 idFunc->setIsStatic( false );
8788 functions << idFunc;
8789
8790 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
8791 currentFeatureFunc->setIsStatic( false );
8792 functions << currentFeatureFunc;
8793
8794 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" ) );
8795 uuidFunc->setIsStatic( false );
8796 functions << uuidFunc;
8797
8798 functions
8799 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
8800 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8801 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
8802 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
8803 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
8804 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8805 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
8806 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
8807
8808 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
8809 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8810 attributesFunc->setIsStatic( false );
8811 functions << attributesFunc;
8812 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
8813 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8814 representAttributesFunc->setIsStatic( false );
8815 functions << representAttributesFunc;
8816
8817 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
8818 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
8819 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
8820 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
8821 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8822 validateFeature->setIsStatic( false );
8823 functions << validateFeature;
8824
8825 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
8826 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
8827 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
8828 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
8829 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
8830 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8831 validateAttribute->setIsStatic( false );
8832 functions << validateAttribute;
8833
8835 QStringLiteral( "maptip" ),
8836 -1,
8837 fcnFeatureMaptip,
8838 QStringLiteral( "Record and Attributes" ),
8839 QString(),
8840 false,
8841 QSet<QString>()
8842 );
8843 maptipFunc->setIsStatic( false );
8844 functions << maptipFunc;
8845
8847 QStringLiteral( "display_expression" ),
8848 -1,
8849 fcnFeatureDisplayExpression,
8850 QStringLiteral( "Record and Attributes" ),
8851 QString(),
8852 false,
8853 QSet<QString>()
8854 );
8855 displayFunc->setIsStatic( false );
8856 functions << displayFunc;
8857
8859 QStringLiteral( "is_selected" ),
8860 -1,
8861 fcnIsSelected,
8862 QStringLiteral( "Record and Attributes" ),
8863 QString(),
8864 false,
8865 QSet<QString>()
8866 );
8867 isSelectedFunc->setIsStatic( false );
8868 functions << isSelectedFunc;
8869
8870 functions
8872 QStringLiteral( "num_selected" ),
8873 -1,
8874 fcnNumSelected,
8875 QStringLiteral( "Record and Attributes" ),
8876 QString(),
8877 false,
8878 QSet<QString>()
8879 );
8880
8881 functions
8883 QStringLiteral( "sqlite_fetch_and_increment" ),
8885 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
8886 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
8887 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
8888 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
8889 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
8890 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
8891 fcnSqliteFetchAndIncrement,
8892 QStringLiteral( "Record and Attributes" )
8893 );
8894
8895 // **Fields and Values** functions
8896 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
8897
8898 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8899 {
8900 Q_UNUSED( context )
8901 if ( node->args()->count() == 1 )
8902 {
8903 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
8904 if ( colRef )
8905 {
8906 return true;
8907 }
8908 else
8909 {
8910 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
8911 return false;
8912 }
8913 }
8914 else if ( node->args()->count() == 2 )
8915 {
8916 return true;
8917 }
8918 else
8919 {
8920 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
8921 return false;
8922 }
8923 }
8924 );
8925
8926 functions << representValueFunc;
8927
8928 // **General** functions
8929 functions
8930 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8931 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
8932 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
8933 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
8935 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8936 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
8937 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
8938 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
8940 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
8941 fcnMimeType, QStringLiteral( "General" ) )
8942 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8943 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
8944 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
8945
8946 // **var** function
8947 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
8948 varFunction->setIsStaticFunction(
8949 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8950 {
8951 /* A variable node is static if it has a static name and the name can be found at prepare
8952 * time and is tagged with isStatic.
8953 * It is not static if a variable is set during iteration or not tagged isStatic.
8954 * (e.g. geom_part variable)
8955 */
8956 if ( node->args()->count() > 0 )
8957 {
8958 QgsExpressionNode *argNode = node->args()->at( 0 );
8959
8960 if ( !argNode->isStatic( parent, context ) )
8961 return false;
8962
8963 const QString varName = argNode->eval( parent, context ).toString();
8964 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
8965 return false;
8966
8967 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
8968 return scope ? scope->isStatic( varName ) : false;
8969 }
8970 return false;
8971 }
8972 );
8973 varFunction->setUsesGeometryFunction(
8974 []( const QgsExpressionNodeFunction * node ) -> bool
8975 {
8976 if ( node && node->args()->count() > 0 )
8977 {
8978 QgsExpressionNode *argNode = node->args()->at( 0 );
8979 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
8980 {
8981 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
8982 return true;
8983 }
8984 }
8985 return false;
8986 }
8987 );
8988
8989 functions
8990 << varFunction;
8991
8992 functions << new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true );
8993
8994 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8995 evalFunc->setIsStaticFunction(
8996 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8997 {
8998 if ( node->args()->count() > 0 )
8999 {
9000 QgsExpressionNode *argNode = node->args()->at( 0 );
9001
9002 if ( argNode->isStatic( parent, context ) )
9003 {
9004 QString expString = argNode->eval( parent, context ).toString();
9005
9006 QgsExpression e( expString );
9007
9008 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9009 return true;
9010 }
9011 }
9012
9013 return false;
9014 } );
9015
9016 functions << evalFunc;
9017
9018 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9019 attributeFunc->setIsStaticFunction(
9020 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9021 {
9022 const QList< QgsExpressionNode *> argList = node->args()->list();
9023 for ( QgsExpressionNode *argNode : argList )
9024 {
9025 if ( !argNode->isStatic( parent, context ) )
9026 return false;
9027 }
9028
9029 if ( node->args()->count() == 1 )
9030 {
9031 // not static -- this is the variant which uses the current feature taken direct from the expression context
9032 return false;
9033 }
9034
9035 return true;
9036 } );
9037 functions << attributeFunc;
9038
9039 functions
9040 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9042 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9043 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9044
9045 // functions for arrays
9048 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9049 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9050 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9051 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9052 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9053 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9054 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9055 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9056 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9057 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9058 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9059 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9060 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9061 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9062 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9063 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9064 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9065 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9066 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9067 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9068 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9069 << 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 )
9070 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9071 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9072 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9073 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9074 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9075 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9076 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9077 << 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" ) )
9078 << 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" ) )
9079 << 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" ) )
9080 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9081
9082 //functions for maps
9083 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9084 << 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" ) )
9085 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9086 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9087 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9088 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9089 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9090 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9091 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9092 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9093 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9094 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9095 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9096 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9097 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9098 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9099 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9100 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9101 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9102 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9103 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9104
9105 ;
9106
9108
9109 //QgsExpression has ownership of all built-in functions
9110 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9111 {
9112 *sOwnedFunctions() << func;
9113 *sBuiltinFunctions() << func->name();
9114 sBuiltinFunctions()->append( func->aliases() );
9115 }
9116 }
9117 return functions;
9118}
9119
9120bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9121{
9122 int fnIdx = functionIndex( function->name() );
9123 if ( fnIdx != -1 )
9124 {
9125 return false;
9126 }
9127 sFunctions()->append( function );
9128 if ( transferOwnership )
9129 sOwnedFunctions()->append( function );
9130 return true;
9131}
9132
9133bool QgsExpression::unregisterFunction( const QString &name )
9134{
9135 // You can never override the built in functions.
9136 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9137 {
9138 return false;
9139 }
9140 int fnIdx = functionIndex( name );
9141 if ( fnIdx != -1 )
9142 {
9143 sFunctions()->removeAt( fnIdx );
9144 return true;
9145 }
9146 return false;
9147}
9148
9150{
9151 qDeleteAll( *sOwnedFunctions() );
9152 sOwnedFunctions()->clear();
9153}
9155const QStringList &QgsExpression::BuiltinFunctions()
9156{
9157 if ( sBuiltinFunctions()->isEmpty() )
9158 {
9159 Functions(); // this method builds the gmBuiltinFunctions as well
9160 }
9161 return *sBuiltinFunctions();
9162}
9164
9166 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9167 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9168 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9169 QStringLiteral( "Arrays" ) )
9170{
9171
9172}
9173
9175{
9176 bool isStatic = false;
9177
9178 QgsExpressionNode::NodeList *args = node->args();
9180 if ( args->count() < 2 )
9181 return false;
9182
9183 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9184 {
9185 isStatic = true;
9186 }
9187 return isStatic;
9188}
9189
9191{
9192 Q_UNUSED( node )
9193 QVariantList result;
9194
9195 if ( args->count() < 2 )
9196 // error
9197 return result;
9198
9199 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9200
9201 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9202 std::unique_ptr< QgsExpressionContext > tempContext;
9203 if ( !subContext )
9204 {
9205 tempContext = std::make_unique< QgsExpressionContext >();
9206 subContext = tempContext.get();
9207 }
9208
9210 subContext->appendScope( subScope );
9211
9212 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
9214 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9215 result << args->at( 1 )->eval( parent, subContext );
9216 }
9217
9218 if ( context )
9219 delete subContext->popScope();
9220
9221 return result;
9222}
9223
9224QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9226 // This is a dummy function, all the real handling is in run
9227 Q_UNUSED( values )
9228 Q_UNUSED( context )
9229 Q_UNUSED( parent )
9230 Q_UNUSED( node )
9231
9232 Q_ASSERT( false );
9233 return QVariant();
9234}
9235
9237{
9238 QgsExpressionNode::NodeList *args = node->args();
9239
9240 if ( args->count() < 2 )
9241 // error
9242 return false;
9243
9244 args->at( 0 )->prepare( parent, context );
9245
9246 QgsExpressionContext subContext;
9247 if ( context )
9248 subContext = *context;
9249
9251 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9252 subContext.appendScope( subScope );
9253
9254 args->at( 1 )->prepare( parent, &subContext );
9255
9256 return true;
9257}
9260 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9261 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9262 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9263 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9264 QStringLiteral( "Arrays" ) )
9265{
9266
9267}
9268
9270{
9271 bool isStatic = false;
9272
9273 QgsExpressionNode::NodeList *args = node->args();
9275 if ( args->count() < 2 )
9276 return false;
9277
9278 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9279 {
9280 isStatic = true;
9281 }
9282 return isStatic;
9283}
9284
9286{
9287 Q_UNUSED( node )
9288 QVariantList result;
9289
9290 if ( args->count() < 2 )
9291 // error
9292 return result;
9293
9294 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9295
9296 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9297 std::unique_ptr< QgsExpressionContext > tempContext;
9298 if ( !subContext )
9299 {
9300 tempContext = std::make_unique< QgsExpressionContext >();
9301 subContext = tempContext.get();
9302 }
9303
9305 subContext->appendScope( subScope );
9306
9307 int limit = 0;
9308 if ( args->count() >= 3 )
9309 {
9310 const QVariant limitVar = args->at( 2 )->eval( parent, context );
9311
9312 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
9313 {
9314 limit = limitVar.toInt();
9315 }
9316 else
9317 {
9318 return result;
9319 }
9320 }
9321
9322 for ( const QVariant &value : array )
9323 {
9324 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
9325 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
9326 {
9327 result << value;
9328
9329 if ( limit > 0 && limit == result.size() )
9330 break;
9331 }
9332 }
9333
9334 if ( context )
9335 delete subContext->popScope();
9336
9337 return result;
9338}
9339
9340QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9342 // This is a dummy function, all the real handling is in run
9343 Q_UNUSED( values )
9344 Q_UNUSED( context )
9345 Q_UNUSED( parent )
9346 Q_UNUSED( node )
9347
9348 Q_ASSERT( false );
9349 return QVariant();
9350}
9351
9353{
9354 QgsExpressionNode::NodeList *args = node->args();
9355
9356 if ( args->count() < 2 )
9357 // error
9358 return false;
9359
9360 args->at( 0 )->prepare( parent, context );
9361
9362 QgsExpressionContext subContext;
9363 if ( context )
9364 subContext = *context;
9365
9367 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9368 subContext.appendScope( subScope );
9369
9370 args->at( 1 )->prepare( parent, &subContext );
9371
9372 return true;
9375 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
9376 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
9377 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
9378 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9379 QStringLiteral( "General" ) )
9380{
9381
9382}
9383
9385{
9386 bool isStatic = false;
9387
9388 QgsExpressionNode::NodeList *args = node->args();
9389
9390 if ( args->count() < 3 )
9391 return false;
9392
9393 // We only need to check if the node evaluation is static, if both - name and value - are static.
9394 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9395 {
9396 QVariant name = args->at( 0 )->eval( parent, context );
9397 QVariant value = args->at( 1 )->eval( parent, context );
9399 // Temporarily append a new scope to provide the variable
9400 appendTemporaryVariable( context, name.toString(), value );
9401 if ( args->at( 2 )->isStatic( parent, context ) )
9402 isStatic = true;
9403 popTemporaryVariable( context );
9404 }
9405
9406 return isStatic;
9407}
9408
9410{
9411 Q_UNUSED( node )
9412 QVariant result;
9413
9414 if ( args->count() < 3 )
9415 // error
9416 return result;
9417
9418 QVariant name = args->at( 0 )->eval( parent, context );
9419 QVariant value = args->at( 1 )->eval( parent, context );
9420
9421 const QgsExpressionContext *updatedContext = context;
9422 std::unique_ptr< QgsExpressionContext > tempContext;
9423 if ( !updatedContext )
9424 {
9425 tempContext = std::make_unique< QgsExpressionContext >();
9426 updatedContext = tempContext.get();
9428
9429 appendTemporaryVariable( updatedContext, name.toString(), value );
9430 result = args->at( 2 )->eval( parent, updatedContext );
9431
9432 if ( context )
9433 popTemporaryVariable( updatedContext );
9434
9435 return result;
9436}
9437
9438QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9440 // This is a dummy function, all the real handling is in run
9441 Q_UNUSED( values )
9442 Q_UNUSED( context )
9443 Q_UNUSED( parent )
9444 Q_UNUSED( node )
9445
9446 Q_ASSERT( false );
9447 return QVariant();
9448}
9449
9451{
9452 QgsExpressionNode::NodeList *args = node->args();
9453
9454 if ( args->count() < 3 )
9455 // error
9456 return false;
9457
9458 QVariant name = args->at( 0 )->prepare( parent, context );
9459 QVariant value = args->at( 1 )->prepare( parent, context );
9460
9461 const QgsExpressionContext *updatedContext = context;
9462 std::unique_ptr< QgsExpressionContext > tempContext;
9463 if ( !updatedContext )
9464 {
9465 tempContext = std::make_unique< QgsExpressionContext >();
9466 updatedContext = tempContext.get();
9467 }
9468
9469 appendTemporaryVariable( updatedContext, name.toString(), value );
9470 args->at( 2 )->prepare( parent, updatedContext );
9471
9472 if ( context )
9473 popTemporaryVariable( updatedContext );
9474
9475 return true;
9476}
9477
9478void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
9479{
9480 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
9481 delete updatedContext->popScope();
9482}
9483
9484void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
9485{
9488
9489 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
9490 updatedContext->appendScope( scope );
9491}
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition: qgis.h:2093
@ 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.
JoinStyle
Join styles for buffers.
Definition: qgis.h:1344
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:1331
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition: qgis.h:2078
@ 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:1357
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
@ PointM
PointM.
@ PointZ
PointZ.
@ PointZM
PointZM.
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
bool is3D() const SIP_HOLDGIL
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.
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 const QgsAbstractGeometry * simplifiedTypeRef() const SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
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.
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
Aggregate
Available aggregates to calculate.
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ StringMaximumLength
Maximum length of string (string fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ ArrayAggregate
Create an array of values.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Median
Median of values (numeric fields only)
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ CountMissing
Number of missing (null) values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ Majority
Majority of values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ Mean
Mean of values (numeric fields only)
@ StringMinimumLength
Minimum length of string (string fields only)
@ CountDistinct
Number of distinct values.
@ Minority
Minority of values.
static 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:44
Abstract base class for color ramps.
Definition: qgscolorramp.h:30
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.
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Q_GADGET Qgis::DistanceUnit mapUnits
QString toProj() const
Returns a Proj string representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Curve polygon geometry type.
int ringCount(int part=0) const override SIP_HOLDGIL
Returns the number of rings of which this geometry is built.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
double roundness() const
Returns the roundness of the curve polygon.
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition: qgscurve.cpp:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
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 convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Ellipse geometry type.
Definition: qgsellipse.h:40
QString what() const
Definition: qgsexception.h:48
Contains utilities for working with EXIF tags in images.
Definition: qgsexiftools.h:33
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
A abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
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 QString helpText(QString name)
Returns the help text for a specified function.
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)
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & 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 & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
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...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
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:56
QgsFields fields
Definition: qgsfeature.h:66
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:219
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:338
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
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:61
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:672
Container of fields for a vector layer.
Definition: qgsfields.h:45
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:359
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
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.
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.
Definition: qgsgeometry.h:164
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...
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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 concaveHull(double targetPercent, bool allowHoles=false) const SIP_THROW(QgsNotSupportedException)
Returns a possibly concave polygon that contains all the points in the geometry.
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.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
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 ...
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.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
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.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
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
Definition: qgsgeometry.h:167
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.
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.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QString lastError() const SIP_HOLDGIL
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const SIP_THROW(QgsNotSupportedException)
Attempts to make an invalid geometry valid without losing vertices.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
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...
QString asWkt(int precision=17) const
Exports the geometry to WKT.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:99
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition: qgsgeos.cpp:2457
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
A representation of the interval between two datetime values.
Definition: qgsinterval.h:42
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:261
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.
Definition: qgsinterval.h:242
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.
Definition: qgslinestring.h:45
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
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.
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString attribution() const
Returns the attribution of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:395
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:78
Qgis::LayerType type
Definition: qgsmaplayer.h:80
virtual bool isEditable() const
Returns true if the layer can be edited.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:359
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:412
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:83
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
Definition: qgsmultipoint.h:30
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.
Definition: qgsexception.h:118
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override SIP_HOLDGIL
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgspoint.cpp:424
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:735
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
double inclination(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition: qgspoint.cpp:723
QgsRelationManager * relationManager
Definition: qgsproject.h:115
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
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) SIP_HOLDGIL
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
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...
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode) SIP_HOLDGIL
Construct a QgsQuadrilateral as a Rectangle from 3 points.
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.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:296
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
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.
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:47
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:46
bool isValid
Definition: qgsrelation.h:49
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
c++ helper class for defining QgsExpression functions.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
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:474
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgssurface.h:43
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
Triangle geometry type.
Definition: qgstriangle.h:34
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
QVariant aggregate(QgsAggregateCalculator::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.
Handles the with_variable(name, value, node) expression function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static QString geometryDisplayString(Qgis::GeometryType type) SIP_HOLDGIL
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:629
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)
Definition: MathUtils.cpp:786
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
uint qHash(const QVariant &variant)
Hash for QVariant.
Definition: qgis.cpp:217
#define str(x)
Definition: qgis.cpp:37
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:119
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:4093
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:4092
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition: qgis.h:3563
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QList< QgsExpressionFunction * > ExpressionFunctionList
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
const QgsField & field
Definition: qgsfield.h:501
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2206
QLineF segment(int index, QRectF rect, double radius)
int precision
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition: qgsogcutils.h:60
const QgsMapLayer * layer
Definition: qgsogcutils.h:70
QgsCoordinateTransformContext transformContext
Definition: qgsogcutils.h:71
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31