QGIS API Documentation 3.99.0-Master (a8882ad4560)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
45#include "qgsmagneticmodel.h"
47#include "qgsmessagelog.h"
48#include "qgsmultilinestring.h"
49#include "qgsmultipoint.h"
50#include "qgsogcutils.h"
51#include "qgspolygon.h"
52#include "qgsproviderregistry.h"
53#include "qgsquadrilateral.h"
54#include "qgsrasterbandstats.h"
55#include "qgsrasterlayer.h"
56#include "qgsregularpolygon.h"
57#include "qgsspatialindex.h"
58#include "qgsstringutils.h"
59#include "qgsstyle.h"
60#include "qgssymbollayerutils.h"
61#include "qgsthreadingutils.h"
62#include "qgstransaction.h"
63#include "qgstriangle.h"
64#include "qgsunittypes.h"
65#include "qgsvariantutils.h"
66#include "qgsvectorlayer.h"
68#include "qgsvectorlayerutils.h"
69
70#include <QCryptographicHash>
71#include <QMimeDatabase>
72#include <QProcessEnvironment>
73#include <QRegularExpression>
74#include <QUrlQuery>
75#include <QUuid>
76
77typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
78
80Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
82
85Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
86
87const QString QgsExpressionFunction::helpText() const
88{
89 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
90}
91
93{
94 Q_UNUSED( node )
95 // evaluate arguments
96 QVariantList argValues;
97 if ( args )
98 {
99 int arg = 0;
100 const QList< QgsExpressionNode * > argList = args->list();
101 argValues.reserve( argList.size() );
102 for ( QgsExpressionNode *n : argList )
103 {
104 QVariant v;
105 if ( lazyEval() )
106 {
107 // Pass in the node for the function to eval as it needs.
108 v = QVariant::fromValue( n );
109 }
110 else
111 {
112 v = n->eval( parent, context );
114 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
115 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
116 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
117 }
118 argValues.append( v );
119 arg++;
120 }
121 }
122
123 return func( argValues, context, parent, node );
124}
125
127{
128 Q_UNUSED( node )
129 return true;
130}
131
133{
134 return QStringList();
135}
136
138{
139 Q_UNUSED( parent )
140 Q_UNUSED( context )
141 Q_UNUSED( node )
142 return false;
143}
144
146{
147 Q_UNUSED( parent )
148 Q_UNUSED( context )
149 Q_UNUSED( node )
150 return true;
151}
152
154{
155 Q_UNUSED( node )
156 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
157}
158
160{
161 return mGroups.isEmpty() ? false : mGroups.contains( u"deprecated"_s );
162}
163
165{
166 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
167}
168
170{
171 return mHandlesNull;
172}
173
174// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
177 FcnEval fcn,
178 const QString &group,
179 const QString &helpText,
180 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
181 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
182 bool lazyEval,
183 const QStringList &aliases,
184 bool handlesNull )
185 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
186 , mFnc( fcn )
187 , mAliases( aliases )
188 , mUsesGeometry( false )
189 , mUsesGeometryFunc( usesGeometry )
190 , mReferencedColumnsFunc( referencedColumns )
191{
192}
194
196{
197 return mAliases;
198}
199
201{
202 if ( mUsesGeometryFunc )
203 return mUsesGeometryFunc( node );
204 else
205 return mUsesGeometry;
206}
207
209{
210 mUsesGeometryFunc = usesGeometry;
211}
212
214{
215 if ( mReferencedColumnsFunc )
216 return mReferencedColumnsFunc( node );
217 else
218 return mReferencedColumns;
219}
220
222{
223 if ( mIsStaticFunc )
224 return mIsStaticFunc( node, parent, context );
225 else
226 return mIsStatic;
227}
228
230{
231 if ( mPrepareFunc )
232 return mPrepareFunc( node, parent, context );
233
234 return true;
235}
236
238{
239 mIsStaticFunc = isStatic;
240}
241
243{
244 mIsStaticFunc = nullptr;
245 mIsStatic = isStatic;
246}
247
248void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
249{
250 mPrepareFunc = prepareFunc;
251}
252
254{
255 if ( node && node->args() )
256 {
257 const QList< QgsExpressionNode * > argList = node->args()->list();
258 for ( QgsExpressionNode *argNode : argList )
259 {
260 if ( !argNode->isStatic( parent, context ) )
261 return false;
262 }
263 }
264
265 return true;
266}
267
268static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
269{
270 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
271 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
272 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
273
274 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
275 return QVariant();
276
277 QVariantList array;
278 int length = 1;
279
280 array << start;
281 double current = start + step;
282 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
283 {
284 array << current;
285 current += step;
286 length++;
287 }
288
289 return array;
290}
291
292static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
293{
294 if ( !context )
295 return QVariant();
296
297 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
298
299 if ( name == "feature"_L1 )
300 {
301 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
302 }
303 else if ( name == "id"_L1 )
304 {
305 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
306 }
307 else if ( name == "geometry"_L1 )
308 {
309 if ( !context->hasFeature() )
310 return QVariant();
311
312 const QgsFeature feature = context->feature();
313 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
314 }
315 else
316 {
317 return context->variable( name );
318 }
319}
320
321static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
322{
323 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
324 return QgsExpression::replaceExpressionText( templateString, context );
325}
326
327static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
328{
329 if ( !context )
330 return QVariant();
331
332 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
333 QgsExpression expression( expString );
334 return expression.evaluate( context );
335}
336
337static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
338{
339 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
340 return QVariant( std::sqrt( x ) );
341}
342
343static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
344{
345 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
346 return QVariant( std::fabs( val ) );
347}
348
349static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
350{
351 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
352 return ( deg * M_PI ) / 180;
353}
354static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
355{
356 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
357 return ( 180 * rad ) / M_PI;
358}
359static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
360{
361 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
362 return QVariant( std::sin( x ) );
363}
364static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
365{
366 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
367 return QVariant( std::cos( x ) );
368}
369static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
370{
371 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
372 return QVariant( std::tan( x ) );
373}
374static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
375{
376 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
377 return QVariant( std::asin( x ) );
378}
379static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
380{
381 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
382 return QVariant( std::acos( x ) );
383}
384static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
385{
386 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
387 return QVariant( std::atan( x ) );
388}
389static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
390{
391 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
392 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
393 return QVariant( std::atan2( y, x ) );
394}
395static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
396{
397 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
398 return QVariant( std::exp( x ) );
399}
400static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
401{
402 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
403 if ( x <= 0 )
404 return QVariant();
405 return QVariant( std::log( x ) );
406}
407static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
408{
409 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
410 if ( x <= 0 )
411 return QVariant();
412 return QVariant( log10( x ) );
413}
414static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
415{
416 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
417 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
418 if ( x <= 0 || b <= 0 )
419 return QVariant();
420 return QVariant( std::log( x ) / std::log( b ) );
421}
422static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
423{
424 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
425 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
426 if ( max < min )
427 return QVariant();
428
429 std::random_device rd;
430 std::mt19937_64 generator( rd() );
431
432 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
433 {
434 quint32 seed;
435 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
436 {
437 // if seed can be converted to int, we use as is
438 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
439 }
440 else
441 {
442 // if not, we hash string representation to int
443 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
444 std::hash<std::string> hasher;
445 seed = hasher( seedStr.toStdString() );
446 }
447 generator.seed( seed );
448 }
449
450 // Return a random double in the range [min, max] (inclusive)
451 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
452 return QVariant( min + f * ( max - min ) );
453}
454static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
455{
456 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
457 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
458 if ( max < min )
459 return QVariant();
460
461 std::random_device rd;
462 std::mt19937_64 generator( rd() );
463
464 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
465 {
466 quint32 seed;
467 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
468 {
469 // if seed can be converted to int, we use as is
470 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
471 }
472 else
473 {
474 // if not, we hash string representation to int
475 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
476 std::hash<std::string> hasher;
477 seed = hasher( seedStr.toStdString() );
478 }
479 generator.seed( seed );
480 }
481
482 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
483 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
484 return QVariant( randomInteger );
485
486 // Prevent wrong conversion of QVariant. See #36412
487 return QVariant( int( randomInteger ) );
488}
489
490static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
491{
492 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
493 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
494 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
495 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
496 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
497
498 if ( domainMin >= domainMax )
499 {
500 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
501 return QVariant();
502 }
503
504 // outside of domain?
505 if ( val >= domainMax )
506 {
507 return rangeMax;
508 }
509 else if ( val <= domainMin )
510 {
511 return rangeMin;
512 }
513
514 // calculate linear scale
515 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
516 double c = rangeMin - ( domainMin * m );
517
518 // Return linearly scaled value
519 return QVariant( m * val + c );
520}
521
522static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
523{
524 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
525 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
526 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
527 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
528 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
529 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
530
531 if ( domainMin >= domainMax )
532 {
533 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
534 return QVariant();
535 }
536 if ( exponent <= 0 )
537 {
538 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
539 return QVariant();
540 }
541
542 // outside of domain?
543 if ( val >= domainMax )
544 {
545 return rangeMax;
546 }
547 else if ( val <= domainMin )
548 {
549 return rangeMin;
550 }
551
552 // Return polynomially scaled value
553 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
554}
555
556static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
557{
558 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
559 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
560 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
561 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
562 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
563 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
564
565 if ( domainMin >= domainMax )
566 {
567 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
568 return QVariant();
569 }
570 if ( exponent <= 0 )
571 {
572 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
573 return QVariant();
574 }
575
576 // outside of domain?
577 if ( val >= domainMax )
578 {
579 return rangeMax;
580 }
581 else if ( val <= domainMin )
582 {
583 return rangeMin;
584 }
585
586 // Return exponentially scaled value
587 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
588 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
589}
590
591static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
592{
593 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
594 double maxVal = std::numeric_limits<double>::quiet_NaN();
595 for ( const QVariant &val : values )
596 {
597 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
598 if ( std::isnan( maxVal ) )
599 {
600 maxVal = testVal;
601 }
602 else if ( !std::isnan( testVal ) )
603 {
604 maxVal = std::max( maxVal, testVal );
605 }
606 }
607
608 if ( !std::isnan( maxVal ) )
609 {
610 result = QVariant( maxVal );
611 }
612 return result;
613}
614
615static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
616{
617 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
618 double minVal = std::numeric_limits<double>::quiet_NaN();
619 for ( const QVariant &val : values )
620 {
621 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
622 if ( std::isnan( minVal ) )
623 {
624 minVal = testVal;
625 }
626 else if ( !std::isnan( testVal ) )
627 {
628 minVal = std::min( minVal, testVal );
629 }
630 }
631
632 if ( !std::isnan( minVal ) )
633 {
634 result = QVariant( minVal );
635 }
636 return result;
637}
638
639static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
640{
641 //lazy eval, so we need to evaluate nodes now
642
643 //first node is layer id or name
644 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
646 QVariant value = node->eval( parent, context );
648
649 // TODO this expression function is NOT thread safe
651 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
653 if ( !vl )
654 {
655 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
656 return QVariant();
657 }
658
659 // second node is aggregate type
660 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
662 value = node->eval( parent, context );
664 bool ok = false;
665 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
666 if ( !ok )
667 {
668 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
669 return QVariant();
670 }
671
672 // third node is subexpression (or field name)
673 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
675 QString subExpression = node->dump();
676
678 //optional forth node is filter
679 if ( values.count() > 3 )
680 {
681 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
683 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
684 if ( !nl || nl->value().isValid() )
685 parameters.filter = node->dump();
686 }
687
688 //optional fifth node is concatenator
689 if ( values.count() > 4 )
690 {
691 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
693 value = node->eval( parent, context );
695 parameters.delimiter = value.toString();
696 }
697
698 //optional sixth node is order by
699 QString orderBy;
700 if ( values.count() > 5 )
701 {
702 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
704 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
705 if ( !nl || nl->value().isValid() )
706 {
707 orderBy = node->dump();
708 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
709 }
710 }
711
712 QString aggregateError;
713 QVariant result;
714 if ( context )
715 {
716 QString cacheKey;
717 QgsExpression subExp( subExpression );
718 QgsExpression filterExp( parameters.filter );
719
720 const QSet< QString > filterVars = filterExp.referencedVariables();
721 const QSet< QString > subExpVars = subExp.referencedVariables();
722 QSet<QString> allVars = filterVars + subExpVars;
723
724 bool isStatic = true;
725 if ( filterVars.contains( u"parent"_s )
726 || filterVars.contains( QString() )
727 || subExpVars.contains( u"parent"_s )
728 || subExpVars.contains( QString() ) )
729 {
730 isStatic = false;
731 }
732 else
733 {
734 for ( const QString &varName : allVars )
735 {
736 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
737 if ( scope && !scope->isStatic( varName ) )
738 {
739 isStatic = false;
740 break;
741 }
742 }
743 }
744
745 if ( isStatic && ! parameters.orderBy.isEmpty() )
746 {
747 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
748 {
749 const QgsExpression &orderByExpression { orderByClause.expression() };
750 if ( orderByExpression.referencedVariables().contains( u"parent"_s ) || orderByExpression.referencedVariables().contains( QString() ) )
751 {
752 isStatic = false;
753 break;
754 }
755 }
756 }
757
758 if ( !isStatic )
759 {
760 bool ok = false;
761 const QString contextHash = context->uniqueHash( ok, allVars );
762 if ( ok )
763 {
764 cacheKey = u"aggfcn:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
765 orderBy, contextHash );
766 }
767 }
768 else
769 {
770 cacheKey = u"aggfcn:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
771 }
772
773 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
774 {
775 return context->cachedValue( cacheKey );
776 }
777
778 QgsExpressionContext subContext( *context );
780 subScope->setVariable( u"parent"_s, context->feature(), true );
781 subContext.appendScope( subScope );
782 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
783
784 if ( ok && !cacheKey.isEmpty() )
785 {
786 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
787 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
788 // associated with it's calculation!
789 context->setCachedValue( cacheKey, result );
790 }
791 }
792 else
793 {
794 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
795 }
796 if ( !ok )
797 {
798 if ( !aggregateError.isEmpty() )
799 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
800 else
801 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
802 return QVariant();
803 }
804
805 return result;
806}
807
808static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
809{
810 if ( !context )
811 {
812 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
813 return QVariant();
814 }
815
816 // first step - find current layer
817
818 // TODO this expression function is NOT thread safe
820 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
822 if ( !vl )
823 {
824 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
825 return QVariant();
826 }
827
828 //lazy eval, so we need to evaluate nodes now
829
830 //first node is relation name
831 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
833 QVariant value = node->eval( parent, context );
835 QString relationId = value.toString();
836 // check relation exists
837 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
838 if ( !relation.isValid() || relation.referencedLayer() != vl )
839 {
840 // check for relations by name
841 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
842 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
843 {
844 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
845 return QVariant();
846 }
847 else
848 {
849 relation = relations.at( 0 );
850 }
851 }
852
853 QgsVectorLayer *childLayer = relation.referencingLayer();
854
855 // second node is aggregate type
856 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
858 value = node->eval( parent, context );
860 bool ok = false;
861 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
862 if ( !ok )
863 {
864 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
865 return QVariant();
866 }
867
868 //third node is subexpression (or field name)
869 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
871 QString subExpression = node->dump();
872
873 //optional fourth node is concatenator
875 if ( values.count() > 3 )
876 {
877 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
879 value = node->eval( parent, context );
881 parameters.delimiter = value.toString();
882 }
883
884 //optional fifth node is order by
885 QString orderBy;
886 if ( values.count() > 4 )
887 {
888 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
890 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
891 if ( !nl || nl->value().isValid() )
892 {
893 orderBy = node->dump();
894 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
895 }
896 }
897
898 if ( !context->hasFeature() )
899 return QVariant();
900 QgsFeature f = context->feature();
901
902 parameters.filter = relation.getRelatedFeaturesFilter( f );
903
904 const QString cacheKey = u"relagg:%1%:%2:%3:%4:%5:%6"_s.arg( relationId, vl->id(),
905 QString::number( static_cast< int >( aggregate ) ),
906 subExpression,
907 parameters.filter,
908 orderBy );
909 if ( context->hasCachedValue( cacheKey ) )
910 return context->cachedValue( cacheKey );
911
912 QVariant result;
913 ok = false;
914
915
916 QgsExpressionContext subContext( *context );
917 QString error;
918 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
919
920 if ( !ok )
921 {
922 if ( !error.isEmpty() )
923 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
924 else
925 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
926 return QVariant();
927 }
928
929 // cache value
930 context->setCachedValue( cacheKey, result );
931 return result;
932}
933
934
935static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
936{
937 if ( !context )
938 {
939 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
940 return QVariant();
941 }
942
943 // first step - find current layer
944
945 // TODO this expression function is NOT thread safe
947 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
949 if ( !vl )
950 {
951 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
952 return QVariant();
953 }
954
955 //lazy eval, so we need to evaluate nodes now
956
957 //first node is subexpression (or field name)
958 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
960 QString subExpression = node->dump();
961
962 //optional second node is group by
963 QString groupBy;
964 if ( values.count() > 1 )
965 {
966 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
968 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
969 if ( !nl || nl->value().isValid() )
970 groupBy = node->dump();
971 }
972
973 //optional third node is filter
974 if ( values.count() > 2 )
975 {
976 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
978 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
979 if ( !nl || nl->value().isValid() )
980 parameters.filter = node->dump();
981 }
982
983 //optional order by node, if supported
984 QString orderBy;
985 if ( orderByPos >= 0 && values.count() > orderByPos )
986 {
987 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
989 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
990 if ( !nl || nl->value().isValid() )
991 {
992 orderBy = node->dump();
993 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
994 }
995 }
996
997 // build up filter with group by
998
999 // find current group by value
1000 if ( !groupBy.isEmpty() )
1001 {
1002 QgsExpression groupByExp( groupBy );
1003 QVariant groupByValue = groupByExp.evaluate( context );
1004 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy,
1005 QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s,
1006 QgsExpression::quotedValue( groupByValue ) );
1007 if ( !parameters.filter.isEmpty() )
1008 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1009 else
1010 parameters.filter = groupByClause;
1011 }
1012
1013 QgsExpression subExp( subExpression );
1014 QgsExpression filterExp( parameters.filter );
1015
1016 bool isStatic = true;
1017 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1018 for ( const QString &varName : refVars )
1019 {
1020 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1021 if ( scope && !scope->isStatic( varName ) )
1022 {
1023 isStatic = false;
1024 break;
1025 }
1026 }
1027
1028 QString cacheKey;
1029 if ( !isStatic )
1030 {
1031 bool ok = false;
1032 const QString contextHash = context->uniqueHash( ok, refVars );
1033 if ( ok )
1034 {
1035 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1036 orderBy, contextHash );
1037 }
1038 }
1039 else
1040 {
1041 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1042 }
1043
1044 if ( context->hasCachedValue( cacheKey ) )
1045 return context->cachedValue( cacheKey );
1046
1047 QVariant result;
1048 bool ok = false;
1049
1050 QgsExpressionContext subContext( *context );
1052 subScope->setVariable( u"parent"_s, context->feature(), true );
1053 subContext.appendScope( subScope );
1054 QString error;
1055 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1056
1057 if ( !ok )
1058 {
1059 if ( !error.isEmpty() )
1060 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1061 else
1062 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1063 return QVariant();
1064 }
1065
1066 // cache value
1067 context->setCachedValue( cacheKey, result );
1068 return result;
1069}
1070
1071
1072static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1073{
1074 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1075}
1076
1077static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1078{
1079 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1080}
1081
1082static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1083{
1084 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1085}
1086
1087static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1088{
1089 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1090}
1091
1092static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1093{
1094 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1095}
1096
1097static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1098{
1099 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1100}
1101
1102static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1103{
1104 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1105}
1106
1107static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1108{
1109 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1110}
1111
1112static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1113{
1114 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1115}
1116
1117static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1118{
1119 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1120}
1121
1122static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1123{
1124 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1125}
1126
1127static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1128{
1129 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1130}
1131
1132static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1133{
1134 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1135}
1136
1137static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1138{
1139 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1140}
1141
1142static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1143{
1144 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1145}
1146
1147static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1148{
1149 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1150}
1151
1152static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1153{
1154 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1155}
1156
1157static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1158{
1159 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1160}
1161
1162static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1163{
1165
1166 //fourth node is concatenator
1167 if ( values.count() > 3 )
1168 {
1169 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1171 QVariant value = node->eval( parent, context );
1173 parameters.delimiter = value.toString();
1174 }
1175
1176 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1177}
1178
1179static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1180{
1182
1183 //fourth node is concatenator
1184 if ( values.count() > 3 )
1185 {
1186 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1188 QVariant value = node->eval( parent, context );
1190 parameters.delimiter = value.toString();
1191 }
1192
1193 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1194}
1195
1196static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1197{
1198 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1199}
1200
1201static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1202{
1203 if ( !context )
1204 return QVariant();
1205
1206 QVariant scale = context->variable( u"map_scale"_s );
1207 bool ok = false;
1208 if ( QgsVariantUtils::isNull( scale ) )
1209 return QVariant();
1210
1211 const double v = scale.toDouble( &ok );
1212 if ( ok )
1213 return v;
1214 return QVariant();
1215}
1216
1217static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1218{
1219 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1220 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1221 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1222
1223 // force testValue to sit inside the range specified by the min and max value
1224 if ( testValue <= minValue )
1225 {
1226 return QVariant( minValue );
1227 }
1228 else if ( testValue >= maxValue )
1229 {
1230 return QVariant( maxValue );
1231 }
1232 else
1233 {
1234 return QVariant( testValue );
1235 }
1236}
1237
1238static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1239{
1240 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1241 return QVariant( std::floor( x ) );
1242}
1243
1244static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1245{
1246 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1247 return QVariant( std::ceil( x ) );
1248}
1249
1250static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1251{
1252 const QVariant value = values.at( 0 );
1253 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1254 {
1255 return QVariant( false );
1256 }
1257 else if ( value.userType() == QMetaType::QString )
1258 {
1259 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1260 return QVariant( !value.toString().isEmpty() );
1261 }
1262 else if ( QgsExpressionUtils::isList( value ) )
1263 {
1264 return !value.toList().isEmpty();
1265 }
1266 return QVariant( value.toBool() );
1267}
1268static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1269{
1270 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1271}
1272static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1273{
1274 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1275}
1276static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1277{
1278 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1279}
1280
1281static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1282{
1283 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1284 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1285 if ( format.isEmpty() && !language.isEmpty() )
1286 {
1287 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1288 return QVariant( QDateTime() );
1289 }
1290
1291 if ( format.isEmpty() && language.isEmpty() )
1292 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1293
1294 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1295 QLocale locale = QLocale();
1296 if ( !language.isEmpty() )
1297 {
1298 locale = QLocale( language );
1299 }
1300
1301 QDateTime datetime = locale.toDateTime( datetimestring, format );
1302 if ( !datetime.isValid() )
1303 {
1304 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1305 datetime = QDateTime();
1306 }
1307 return QVariant( datetime );
1308}
1309
1310static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1311{
1312 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1313 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1314 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1315
1316 const QDate date( year, month, day );
1317 if ( !date.isValid() )
1318 {
1319 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1320 return QVariant();
1321 }
1322 return QVariant( date );
1323}
1324
1325static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1326{
1327 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1328 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1329 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1330
1331 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1332 if ( !time.isValid() )
1333 {
1334 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1335 return QVariant();
1336 }
1337 return QVariant( time );
1338}
1339
1340static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1341{
1342 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1343 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1344 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1345 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1346 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1347 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1348
1349 const QDate date( year, month, day );
1350 if ( !date.isValid() )
1351 {
1352 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1353 return QVariant();
1354 }
1355 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1356 if ( !time.isValid() )
1357 {
1358 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1359 return QVariant();
1360 }
1361 return QVariant( QDateTime( date, time ) );
1362}
1363
1364static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1365{
1366 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1367
1368 QTimeZone tz;
1369
1370#if QT_FEATURE_timezone > 0
1371 if ( !timeZoneId.isEmpty() )
1372 {
1373 tz = QTimeZone( timeZoneId.toUtf8() );
1374 }
1375
1376 if ( !tz.isValid() )
1377 {
1378 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1379 return QVariant();
1380 }
1381
1382#else
1383 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1384#endif
1385 return QVariant::fromValue( tz );
1386}
1387
1388static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1389{
1390#if QT_FEATURE_timezone > 0
1391 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1392 if ( datetime.isValid() )
1393 {
1394 return QVariant::fromValue( datetime.timeZone() );
1395 }
1396 return QVariant();
1397#else
1398 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1399 return QVariant();
1400#endif
1401}
1402
1403static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1404{
1405#if QT_FEATURE_timezone > 0
1406 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1407 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1408 if ( datetime.isValid() && tz.isValid() )
1409 {
1410 datetime.setTimeZone( tz );
1411 return QVariant::fromValue( datetime );
1412 }
1413 return QVariant();
1414#else
1415 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1416 return QVariant();
1417#endif
1418}
1419
1420static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1421{
1422#if QT_FEATURE_timezone > 0
1423 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1424 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1425 if ( datetime.isValid() && tz.isValid() )
1426 {
1427 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1428 }
1429 return QVariant();
1430#else
1431 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1432 return QVariant();
1433#endif
1434}
1435
1436static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1437{
1438#if QT_FEATURE_timezone > 0
1439 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1440 if ( timeZone.isValid() )
1441 {
1442 return QString( timeZone.id() );
1443 }
1444 return QVariant();
1445#else
1446 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1447 return QVariant();
1448#endif
1449}
1450
1451static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1452{
1453 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1454 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1455 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1456 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1457 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1458 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1459 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1460
1461 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1462}
1463
1464static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1465{
1466 for ( const QVariant &value : values )
1467 {
1468 if ( QgsVariantUtils::isNull( value ) )
1469 continue;
1470 return value;
1471 }
1472 return QVariant();
1473}
1474
1475static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1476{
1477 const QVariant val1 = values.at( 0 );
1478 const QVariant val2 = values.at( 1 );
1479
1480 if ( val1 == val2 )
1481 return QVariant();
1482 else
1483 return val1;
1484}
1485
1486static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1487{
1488 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1489 return QVariant( str.toLower() );
1490}
1491static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1492{
1493 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1494 return QVariant( str.toUpper() );
1495}
1496static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1497{
1498 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1499 QStringList elems = str.split( ' ' );
1500 for ( int i = 0; i < elems.size(); i++ )
1501 {
1502 if ( elems[i].size() > 1 )
1503 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1504 }
1505 return QVariant( elems.join( ' '_L1 ) );
1506}
1507
1508static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1509{
1510 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1511 return QVariant( str.trimmed() );
1512}
1513
1514static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1515{
1516 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1517
1518 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1519
1520 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1521 str.replace( re, QString() );
1522 return QVariant( str );
1523}
1524
1525static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1526{
1527 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1528
1529 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1530
1531 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1532 str.replace( re, QString() );
1533 return QVariant( str );
1534}
1535
1536static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1537{
1538 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1539 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1540 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1541}
1542
1543static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1544{
1545 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1546 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1547 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1548}
1549
1550static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1551{
1552 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1553 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1554 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1555 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1556}
1557
1558static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1559{
1560 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1561 return QVariant( QgsStringUtils::soundex( string ) );
1562}
1563
1564static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1565{
1566 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1567 return QVariant( QString( character ) );
1568}
1569
1570static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1571{
1572 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1573
1574 if ( value.isEmpty() )
1575 {
1576 return QVariant();
1577 }
1578
1579 int res = value.at( 0 ).unicode();
1580 return QVariant( res );
1581}
1582
1583static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1584{
1585 if ( values.length() == 2 || values.length() == 3 )
1586 {
1587 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1588 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1589
1590 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1591
1592 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1593 }
1594
1595 return QVariant();
1596}
1597
1598static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1599{
1600 // two variants, one for geometry, one for string
1601
1602 //geometry variant
1603 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1604 if ( !geom.isNull() )
1605 {
1606 if ( geom.type() == Qgis::GeometryType::Line )
1607 return QVariant( geom.length() );
1608 else
1609 return QVariant();
1610 }
1611
1612 //otherwise fall back to string variant
1613 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1614 return QVariant( str.length() );
1615}
1616
1617static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1618{
1619 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1620
1621 if ( geom.type() != Qgis::GeometryType::Line )
1622 return QVariant();
1623
1624 double totalLength = 0;
1625 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1626 {
1628 {
1629 totalLength += line->length3D();
1630 }
1631 else
1632 {
1633 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1634 totalLength += segmentized->length3D();
1635 }
1636 }
1637
1638 return totalLength;
1639}
1640
1641
1642static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1643{
1644 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1645 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1646 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1647}
1648
1649static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1650{
1651 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1652 {
1653 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1654 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1655 QVector< QPair< QString, QString > > mapItems;
1656
1657 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1658 {
1659 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1660 }
1661
1662 // larger keys should be replaced first since they may contain whole smaller keys
1663 std::sort( mapItems.begin(),
1664 mapItems.end(),
1665 []( const QPair< QString, QString > &pair1,
1666 const QPair< QString, QString > &pair2 )
1667 {
1668 return ( pair1.first.length() > pair2.first.length() );
1669 } );
1670
1671 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1672 {
1673 str = str.replace( it->first, it->second );
1674 }
1675
1676 return QVariant( str );
1677 }
1678 else if ( values.count() == 3 )
1679 {
1680 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1681 QVariantList before;
1682 QVariantList after;
1683 bool isSingleReplacement = false;
1684
1685 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1686 {
1687 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1688 }
1689 else
1690 {
1691 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1692 }
1693
1694 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1695 {
1696 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1697 isSingleReplacement = true;
1698 }
1699 else
1700 {
1701 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1702 }
1703
1704 if ( !isSingleReplacement && before.length() != after.length() )
1705 {
1706 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1707 return QVariant();
1708 }
1709
1710 for ( int i = 0; i < before.length(); i++ )
1711 {
1712 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1713 }
1714
1715 return QVariant( str );
1716 }
1717 else
1718 {
1719 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1720 return QVariant();
1721 }
1722}
1723
1724static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1725{
1726 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1727 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1728 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1729
1730 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1731 if ( !re.isValid() )
1732 {
1733 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1734 return QVariant();
1735 }
1736 return QVariant( str.replace( re, after ) );
1737}
1738
1739static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1740{
1741 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1742 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1743
1744 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1745 if ( !re.isValid() )
1746 {
1747 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1748 return QVariant();
1749 }
1750 return QVariant( ( str.indexOf( re ) + 1 ) );
1751}
1752
1753static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1754{
1755 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1756 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1757 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1758
1759 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1760 if ( !re.isValid() )
1761 {
1762 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1763 return QVariant();
1764 }
1765
1766 QRegularExpressionMatch matches = re.match( str );
1767 if ( matches.hasMatch() )
1768 {
1769 QVariantList array;
1770 QStringList list = matches.capturedTexts();
1771
1772 // Skip the first string to only return captured groups
1773 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1774 {
1775 array += ( !( *it ).isEmpty() ) ? *it : empty;
1776 }
1777
1778 return QVariant( array );
1779 }
1780 else
1781 {
1782 return QVariant();
1783 }
1784}
1785
1786static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1787{
1788 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1789 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1790
1791 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1792 if ( !re.isValid() )
1793 {
1794 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1795 return QVariant();
1796 }
1797
1798 // extract substring
1799 QRegularExpressionMatch match = re.match( str );
1800 if ( match.hasMatch() )
1801 {
1802 // return first capture
1803 if ( match.lastCapturedIndex() > 0 )
1804 {
1805 // a capture group was present, so use that
1806 return QVariant( match.captured( 1 ) );
1807 }
1808 else
1809 {
1810 // no capture group, so using all match
1811 return QVariant( match.captured( 0 ) );
1812 }
1813 }
1814 else
1815 {
1816 return QVariant( "" );
1817 }
1818}
1819
1820static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1821{
1822 QString uuid = QUuid::createUuid().toString();
1823 if ( values.at( 0 ).toString().compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1824 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1825 else if ( values.at( 0 ).toString().compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1826 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1827 return uuid;
1828}
1829
1830static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1831{
1832 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1833 return QVariant();
1834
1835 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1836 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1837
1838 int len = 0;
1839 if ( values.at( 2 ).isValid() )
1840 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1841 else
1842 len = str.size();
1843
1844 if ( from < 0 )
1845 {
1846 from = str.size() + from;
1847 if ( from < 0 )
1848 {
1849 from = 0;
1850 }
1851 }
1852 else if ( from > 0 )
1853 {
1854 //account for the fact that substr() starts at 1
1855 from -= 1;
1856 }
1857
1858 if ( len < 0 )
1859 {
1860 len = str.size() + len - from;
1861 if ( len < 0 )
1862 {
1863 len = 0;
1864 }
1865 }
1866
1867 return QVariant( str.mid( from, len ) );
1868}
1869static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1870{
1871 FEAT_FROM_CONTEXT( context, f )
1872 return QVariant( f.id() );
1873}
1874
1875static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1876{
1877 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1878 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1879 bool foundLayer = false;
1880 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1881 {
1882 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1883 if ( !layer || !layer->dataProvider() )
1884 {
1885 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1886 return QVariant();
1887 }
1888
1889 if ( bandNb < 1 || bandNb > layer->bandCount() )
1890 {
1891 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1892 return QVariant();
1893 }
1894
1895 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1896 {
1897 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1898 return QVariant();
1899 }
1900
1901 QgsPointXY point = geom.asPoint();
1902 if ( geom.isMultipart() )
1903 {
1904 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1905 if ( multiPoint.count() == 1 )
1906 {
1907 point = multiPoint[0];
1908 }
1909 else
1910 {
1911 // if the geometry contains more than one part, return an undefined value
1912 return QVariant();
1913 }
1914 }
1915
1916 double value = layer->dataProvider()->sample( point, bandNb );
1917 return std::isnan( value ) ? QVariant() : value;
1918 },
1919 foundLayer );
1920
1921 if ( !foundLayer )
1922 {
1923 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1924 return QVariant();
1925 }
1926 else
1927 {
1928 return res;
1929 }
1930}
1931
1932static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1933{
1934 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1935 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1936
1937 bool foundLayer = false;
1938 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1939 {
1940 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1941 if ( !layer || !layer->dataProvider() )
1942 {
1943 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1944 return QVariant();
1945 }
1946
1947 if ( bandNb < 1 || bandNb > layer->bandCount() )
1948 {
1949 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1950 return QVariant();
1951 }
1952
1953 if ( std::isnan( value ) )
1954 {
1955 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1956 return QVariant();
1957 }
1958
1959 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1960 {
1961 return QVariant();
1962 }
1963
1964 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1965 if ( data.isEmpty() )
1966 {
1967 return QVariant();
1968 }
1969
1970 QVariantMap result;
1971 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1972 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1973 {
1974 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1975 if ( field.isColor() || field.isRamp() )
1976 {
1977 continue;
1978 }
1979 result.insert( fields.at( idx ).name, data.at( idx ) );
1980 }
1981
1982 return result;
1983 }, foundLayer );
1984
1985 if ( !foundLayer )
1986 {
1987 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1988 return QVariant();
1989 }
1990 else
1991 {
1992 return res;
1993 }
1994}
1995
1996static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1997{
1998 if ( !context )
1999 return QVariant();
2000
2001 return context->feature();
2002}
2003
2004static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2005{
2006 QgsFeature feature;
2007 QString attr;
2008 if ( values.size() == 1 )
2009 {
2010 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2011 feature = context->feature();
2012 }
2013 else if ( values.size() == 2 )
2014 {
2015 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2016 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2017 }
2018 else
2019 {
2020 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2021 return QVariant();
2022 }
2023
2024 return feature.attribute( attr );
2025}
2026
2027static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2028{
2029 QString table { R"html(
2030 <table>
2031 <thead>
2032 <tr><th>%1</th></tr>
2033 </thead>
2034 <tbody>
2035 <tr><td>%2</td></tr>
2036 </tbody>
2037 </table>)html" };
2038 QVariantMap dict;
2039 if ( values.size() == 1 )
2040 {
2041 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2042 }
2043 else
2044 {
2045 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2046 return QVariant();
2047 }
2048
2049 if ( dict.isEmpty() )
2050 {
2051 return QVariant();
2052 }
2053
2054 QStringList headers;
2055 QStringList cells;
2056
2057 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2058 {
2059 headers.push_back( it.key().toHtmlEscaped() );
2060 cells.push_back( it.value().toString( ).toHtmlEscaped() );
2061 }
2062
2063 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2064}
2065
2066static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2067{
2068 QString table { R"html(
2069 <dl>
2070 %1
2071 </dl>)html" };
2072 QVariantMap dict;
2073 if ( values.size() == 1 )
2074 {
2075 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2076 }
2077 else
2078 {
2079 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2080 return QVariant();
2081 }
2082
2083 if ( dict.isEmpty() )
2084 {
2085 return QVariant();
2086 }
2087
2088 QString rows;
2089
2090 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2091 {
2092 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2093 }
2094
2095 return table.arg( rows );
2096}
2097
2098static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2099{
2100 QVariant layer;
2101 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2102 {
2103 layer = context->variable( u"layer"_s );
2104 }
2105 else
2106 {
2107 //first node is layer id or name
2108 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2110 layer = node->eval( parent, context );
2112 }
2113
2114 QgsFeature feature;
2115 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2116 {
2117 feature = context->feature();
2118 }
2119 else
2120 {
2121 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2122 }
2123
2125 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2126 if ( strength == "hard"_L1 )
2127 {
2129 }
2130 else if ( strength == "soft"_L1 )
2131 {
2133 }
2134
2135 bool foundLayer = false;
2136 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2137 {
2138 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2139 if ( !layer )
2140 {
2141 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2142 return QVariant();
2143 }
2144
2145 const QgsFields fields = layer->fields();
2146 bool valid = true;
2147 for ( int i = 0; i < fields.size(); i++ )
2148 {
2149 QStringList errors;
2150 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2151 if ( !valid )
2152 {
2153 break;
2154 }
2155 }
2156
2157 return valid;
2158 }, foundLayer );
2159
2160 if ( !foundLayer )
2161 {
2162 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2163 return QVariant();
2164 }
2165
2166 return res;
2167}
2168
2169static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2170{
2171 QVariant layer;
2172 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2173 {
2174 layer = context->variable( u"layer"_s );
2175 }
2176 else
2177 {
2178 //first node is layer id or name
2179 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2181 layer = node->eval( parent, context );
2183 }
2184
2185 QgsFeature feature;
2186 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2187 {
2188 feature = context->feature();
2189 }
2190 else
2191 {
2192 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2193 }
2194
2196 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2197 if ( strength == "hard"_L1 )
2198 {
2200 }
2201 else if ( strength == "soft"_L1 )
2202 {
2204 }
2205
2206 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2207
2208 bool foundLayer = false;
2209 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2210 {
2211 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2212 if ( !layer )
2213 {
2214 return QVariant();
2215 }
2216
2217 const int fieldIndex = layer->fields().indexFromName( attributeName );
2218 if ( fieldIndex == -1 )
2219 {
2220 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2221 return QVariant();
2222 }
2223
2224 QStringList errors;
2225 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2226 return valid;
2227 }, foundLayer );
2228
2229 if ( !foundLayer )
2230 {
2231 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2232 return QVariant();
2233 }
2234
2235 return res;
2236}
2237
2238static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2239{
2240 QgsFeature feature;
2241 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2242 {
2243 feature = context->feature();
2244 }
2245 else
2246 {
2247 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2248 }
2249
2250 const QgsFields fields = feature.fields();
2251 QVariantMap result;
2252 for ( int i = 0; i < fields.count(); ++i )
2253 {
2254 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2255 }
2256 return result;
2257}
2258
2259static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2260{
2261 QgsVectorLayer *layer = nullptr;
2262 QgsFeature feature;
2263
2264 // TODO this expression function is NOT thread safe
2266 if ( values.isEmpty() )
2267 {
2268 feature = context->feature();
2269 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2270 }
2271 else if ( values.size() == 1 )
2272 {
2273 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2274 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2275 }
2276 else if ( values.size() == 2 )
2277 {
2278 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2279 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2280 }
2281 else
2282 {
2283 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2284 return QVariant();
2285 }
2287
2288 if ( !layer )
2289 {
2290 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2291 return QVariant();
2292 }
2293
2294 if ( !feature.isValid() )
2295 {
2296 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2297 return QVariant();
2298 }
2299
2300 const QgsFields fields = feature.fields();
2301 QVariantMap result;
2302 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2303 {
2304 const QString fieldName { fields.at( fieldIndex ).name() };
2305 const QVariant attributeVal = feature.attribute( fieldIndex );
2306 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2307 if ( context && context->hasCachedValue( cacheValueKey ) )
2308 {
2309 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2310 }
2311 else
2312 {
2313 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2315 QVariant cache;
2316 if ( context )
2317 {
2318 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2319
2320 if ( !context->hasCachedValue( cacheKey ) )
2321 {
2322 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2323 context->setCachedValue( cacheKey, cache );
2324 }
2325 else
2326 {
2327 cache = context->cachedValue( cacheKey );
2328 }
2329 }
2330 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2331
2332 result.insert( fields.at( fieldIndex ).name(), value );
2333
2334 if ( context )
2335 {
2336 context->setCachedValue( cacheValueKey, value );
2337 }
2338
2339 }
2340 }
2341 return result;
2342}
2343
2344static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2345{
2346 QgsVectorLayer *layer = nullptr;
2347 QgsFeature feature;
2348 bool evaluate = true;
2349
2350 // TODO this expression function is NOT thread safe
2352 if ( values.isEmpty() )
2353 {
2354 feature = context->feature();
2355 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2356 }
2357 else if ( values.size() == 1 )
2358 {
2359 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2360 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2361 }
2362 else if ( values.size() == 2 )
2363 {
2364 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2365 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2366 }
2367 else if ( values.size() == 3 )
2368 {
2369 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2370 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2371 evaluate = values.value( 2 ).toBool();
2372 }
2373 else
2374 {
2375 if ( isMaptip )
2376 {
2377 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2378 }
2379 else
2380 {
2381 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2382 }
2383 return QVariant();
2384 }
2385
2386 if ( !layer )
2387 {
2388 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2389 return QVariant( );
2390 }
2392
2393 if ( !feature.isValid() )
2394 {
2395 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2396 return QVariant( );
2397 }
2398
2399 if ( ! evaluate )
2400 {
2401 if ( isMaptip )
2402 {
2403 return layer->mapTipTemplate();
2404 }
2405 else
2406 {
2407 return layer->displayExpression();
2408 }
2409 }
2410
2411 QgsExpressionContext subContext( *context );
2412 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2413 subContext.setFeature( feature );
2414
2415 if ( isMaptip )
2416 {
2417 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2418 }
2419 else
2420 {
2421 QgsExpression exp( layer->displayExpression() );
2422 exp.prepare( &subContext );
2423 return exp.evaluate( &subContext ).toString();
2424 }
2425}
2426
2427static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2428{
2429 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2430}
2431
2432static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2433{
2434 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2435}
2436
2437static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2438{
2439 QgsFeature feature;
2440 QVariant layer;
2441 if ( values.isEmpty() )
2442 {
2443 feature = context->feature();
2444 layer = context->variable( u"layer"_s );
2445 }
2446 else if ( values.size() == 1 )
2447 {
2448 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2449 layer = context->variable( u"layer"_s );
2450 }
2451 else if ( values.size() == 2 )
2452 {
2453 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2454 layer = values.at( 0 );
2455 }
2456 else
2457 {
2458 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2459 return QVariant();
2460 }
2461
2462 bool foundLayer = false;
2463 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2464 {
2465 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2466 if ( !layer || !feature.isValid() )
2467 {
2468 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2469 }
2470
2471 return layer->selectedFeatureIds().contains( feature.id() );
2472 }, foundLayer );
2473 if ( !foundLayer )
2474 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2475 else
2476 return res;
2477}
2478
2479static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2480{
2481 QVariant layer;
2482
2483 if ( values.isEmpty() )
2484 layer = context->variable( u"layer"_s );
2485 else if ( values.count() == 1 )
2486 layer = values.at( 0 );
2487 else
2488 {
2489 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2490 return QVariant();
2491 }
2492
2493 bool foundLayer = false;
2494 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2495 {
2496 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2497 if ( !layer )
2498 {
2499 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2500 }
2501
2502 return layer->selectedFeatureCount();
2503 }, foundLayer );
2504 if ( !foundLayer )
2505 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2506 else
2507 return res;
2508}
2509
2510static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2511{
2512 static QMap<QString, qlonglong> counterCache;
2513 QVariant functionResult;
2514
2515 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2516 {
2517 QString database;
2518
2519 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2520
2521 if ( layer )
2522 {
2523 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2524 database = decodedUri.value( u"path"_s ).toString();
2525 if ( database.isEmpty() )
2526 {
2527 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2528 }
2529 }
2530 else
2531 {
2532 database = databaseArgument;
2533 }
2534
2535 const QString table = values.at( 1 ).toString();
2536 const QString idColumn = values.at( 2 ).toString();
2537 const QString filterAttribute = values.at( 3 ).toString();
2538 const QVariant filterValue = values.at( 4 ).toString();
2539 const QVariantMap defaultValues = values.at( 5 ).toMap();
2540
2541 // read from database
2543 sqlite3_statement_unique_ptr sqliteStatement;
2544
2545 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2546 {
2547 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2548 functionResult = QVariant();
2549 return;
2550 }
2551
2552 QString errorMessage;
2553 QString currentValSql;
2554
2555 qlonglong nextId = 0;
2556 bool cachedMode = false;
2557 bool valueRetrieved = false;
2558
2559 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2560
2561 // Running in transaction mode, check for cached value first
2562 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2563 {
2564 cachedMode = true;
2565
2566 auto cachedCounter = counterCache.find( cacheString );
2567
2568 if ( cachedCounter != counterCache.end() )
2569 {
2570 qlonglong &cachedValue = cachedCounter.value();
2571 nextId = cachedValue;
2572 nextId += 1;
2573 cachedValue = nextId;
2574 valueRetrieved = true;
2575 }
2576 }
2577
2578 // Either not in cached mode or no cached value found, obtain from DB
2579 if ( !cachedMode || !valueRetrieved )
2580 {
2581 int result = SQLITE_ERROR;
2582
2583 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2584 if ( !filterAttribute.isNull() )
2585 {
2586 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2587 }
2588
2589 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2590
2591 if ( result == SQLITE_OK )
2592 {
2593 nextId = 0;
2594 if ( sqliteStatement.step() == SQLITE_ROW )
2595 {
2596 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2597 }
2598
2599 // If in cached mode: add value to cache and connect to transaction
2600 if ( cachedMode && result == SQLITE_OK )
2601 {
2602 counterCache.insert( cacheString, nextId );
2603
2604 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2605 {
2606 counterCache.remove( cacheString );
2607 } );
2608 }
2609 valueRetrieved = true;
2610 }
2611 }
2612
2613 if ( valueRetrieved )
2614 {
2615 QString upsertSql;
2616 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2617 QStringList cols;
2618 QStringList vals;
2619 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2620 vals << QgsSqliteUtils::quotedValue( nextId );
2621
2622 if ( !filterAttribute.isNull() )
2623 {
2624 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2625 vals << QgsSqliteUtils::quotedValue( filterValue );
2626 }
2627
2628 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2629 {
2630 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2631 vals << iter.value().toString();
2632 }
2633
2634 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2635 upsertSql += " VALUES "_L1;
2636 upsertSql += '(' + vals.join( ',' ) + ')';
2637
2638 int result = SQLITE_ERROR;
2639 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2640 {
2641 QgsTransaction *transaction = layer->dataProvider()->transaction();
2642 if ( transaction->executeSql( upsertSql, errorMessage ) )
2643 {
2644 result = SQLITE_OK;
2645 }
2646 }
2647 else
2648 {
2649 result = sqliteDb.exec( upsertSql, errorMessage );
2650 }
2651 if ( result == SQLITE_OK )
2652 {
2653 functionResult = QVariant( nextId );
2654 return;
2655 }
2656 else
2657 {
2658 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2659 functionResult = QVariant();
2660 return;
2661 }
2662 }
2663
2664 functionResult = QVariant();
2665 };
2666
2667 bool foundLayer = false;
2668 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2669 {
2670 fetchAndIncrementFunc( layer, QString() );
2671 }, foundLayer );
2672 if ( !foundLayer )
2673 {
2674 const QString databasePath = values.at( 0 ).toString();
2675 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2676 {
2677 fetchAndIncrementFunc( nullptr, databasePath );
2678 } );
2679 }
2680
2681 return functionResult;
2682}
2683
2684static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2685{
2686 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2687 if ( !crs.isValid() )
2688 return QVariant();
2689 return crs.authid();
2690}
2691
2692static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2693{
2694 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2695 QgsCoordinateReferenceSystem crs( definition );
2696
2697 if ( !crs.isValid() )
2698 {
2699 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2700 }
2701
2702 return QVariant::fromValue( crs );
2703}
2704
2705static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2706{
2707 QString concat;
2708 for ( const QVariant &value : values )
2709 {
2710 if ( !QgsVariantUtils::isNull( value ) )
2711 concat += QgsExpressionUtils::getStringValue( value, parent );
2712 }
2713 return concat;
2714}
2715
2716static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2717{
2718 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2719 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2720}
2721
2722static QVariant fcnUnaccent(
2723 const QVariantList &values,
2724 const QgsExpressionContext *context,
2725 QgsExpression *,
2726 const QgsExpressionNodeFunction *node
2727)
2728{
2729 Q_UNUSED( context )
2730 Q_UNUSED( node )
2731
2732 if ( values.isEmpty() || values[0].isNull() )
2733 return QVariant();
2734
2735 return QgsStringUtils::unaccent( values[0].toString() );
2736}
2737
2738
2739static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2740{
2741 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2742 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2743 return string.right( pos );
2744}
2745
2746static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2747{
2748 if ( values.length() < 2 || values.length() > 3 )
2749 return QVariant();
2750
2751 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2752 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2753
2754 bool overlapping = false;
2755 if ( values.length() == 3 )
2756 {
2757 overlapping = values.at( 2 ).toBool();
2758 }
2759
2760 if ( substring.isEmpty() )
2761 return QVariant( 0 );
2762
2763 int count = 0;
2764 if ( overlapping )
2765 {
2766 count = input.count( substring );
2767 }
2768 else
2769 {
2770 int pos = 0;
2771 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2772 {
2773 count++;
2774 pos += substring.length();
2775 }
2776 }
2777
2778 return QVariant( count );
2779}
2780
2781static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2782{
2783 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2784 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2785 return string.left( pos );
2786}
2787
2788static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2789{
2790 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2791 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2792 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2793 return string.leftJustified( length, fill.at( 0 ), true );
2794}
2795
2796static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2797{
2798 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2799 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2800 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2801 return string.rightJustified( length, fill.at( 0 ), true );
2802}
2803
2804static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2805{
2806 if ( values.size() < 1 )
2807 {
2808 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2809 return QVariant();
2810 }
2811
2812 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2813 for ( int n = 1; n < values.length(); n++ )
2814 {
2815 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2816 }
2817 return string;
2818}
2819
2820
2821static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2822{
2823 return QVariant( QDateTime::currentDateTime() );
2824}
2825
2826static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2827{
2828 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2829 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2830 if ( format.isEmpty() && !language.isEmpty() )
2831 {
2832 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2833 return QVariant( QDate() );
2834 }
2835
2836 if ( format.isEmpty() && language.isEmpty() )
2837 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2838
2839 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2840 QLocale locale = QLocale();
2841 if ( !language.isEmpty() )
2842 {
2843 locale = QLocale( language );
2844 }
2845
2846 QDate date = locale.toDate( datestring, format );
2847 if ( !date.isValid() )
2848 {
2849 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2850 date = QDate();
2851 }
2852 return QVariant( date );
2853}
2854
2855static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2856{
2857 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2858 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2859 if ( format.isEmpty() && !language.isEmpty() )
2860 {
2861 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2862 return QVariant( QTime() );
2863 }
2864
2865 if ( format.isEmpty() && language.isEmpty() )
2866 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2867
2868 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2869 QLocale locale = QLocale();
2870 if ( !language.isEmpty() )
2871 {
2872 locale = QLocale( language );
2873 }
2874
2875 QTime time = locale.toTime( timestring, format );
2876 if ( !time.isValid() )
2877 {
2878 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2879 time = QTime();
2880 }
2881 return QVariant( time );
2882}
2883
2884static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2885{
2886 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2887}
2888
2889/*
2890 * DMS functions
2891 */
2892
2893static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2894{
2895 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2896 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2897 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2898
2899 QString formatString;
2900 if ( values.count() > 3 )
2901 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2902
2904 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
2905 {
2907 }
2908 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
2909 {
2911 }
2912 else if ( ! formatString.isEmpty() )
2913 {
2914 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2915 return QVariant();
2916 }
2917
2918 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
2919 {
2920 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2921 }
2922 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
2923 {
2924 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2925 }
2926 else
2927 {
2928 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2929 return QVariant();
2930 }
2931}
2932
2933static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2934{
2936 return floatToDegreeFormat( format, values, context, parent, node );
2937}
2938
2939static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2940{
2941 double value = 0.0;
2942 bool ok = false;
2943 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2944
2945 return ok ? QVariant( value ) : QVariant();
2946}
2947
2948static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2949{
2951 return floatToDegreeFormat( format, values, context, parent, node );
2952}
2953
2954static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2955{
2956 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2957 return static_cast< int >( decimalDegrees );
2958}
2959
2960static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2961{
2962 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2963 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2964 return static_cast< int >( remainder * 60 );
2965}
2966
2967static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2968{
2969 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2970 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2971 const double remainderInMinutes = remainder * 60;
2972 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
2973 // do not truncate to int, this function returns decimal seconds!
2974 return remainderSecondsFraction * 60;
2975}
2976
2977static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2978{
2979 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2980 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2981 qint64 seconds = d2.secsTo( d1 );
2982 return QVariant::fromValue( QgsInterval( seconds ) );
2983}
2984
2985static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2986{
2987 if ( !values.at( 0 ).canConvert<QDate>() )
2988 return QVariant();
2989
2990 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2991 if ( !date.isValid() )
2992 return QVariant();
2993
2994 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2995 // (to match PostgreSQL behavior)
2996 return date.dayOfWeek() % 7;
2997}
2998
2999static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3000{
3001 QVariant value = values.at( 0 );
3002 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3003 if ( inter.isValid() )
3004 {
3005 return QVariant( inter.days() );
3006 }
3007 else
3008 {
3009 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3010 return QVariant( d1.date().day() );
3011 }
3012}
3013
3014static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3015{
3016 QVariant value = values.at( 0 );
3017 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3018 if ( inter.isValid() )
3019 {
3020 return QVariant( inter.years() );
3021 }
3022 else
3023 {
3024 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3025 return QVariant( d1.date().year() );
3026 }
3027}
3028
3029static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3030{
3031 QVariant value = values.at( 0 );
3032 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3033 if ( inter.isValid() )
3034 {
3035 return QVariant( inter.months() );
3036 }
3037 else
3038 {
3039 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3040 return QVariant( d1.date().month() );
3041 }
3042}
3043
3044static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3045{
3046 QVariant value = values.at( 0 );
3047 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3048 if ( inter.isValid() )
3049 {
3050 return QVariant( inter.weeks() );
3051 }
3052 else
3053 {
3054 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3055 return QVariant( d1.date().weekNumber() );
3056 }
3057}
3058
3059static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3060{
3061 QVariant value = values.at( 0 );
3062 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3063 if ( inter.isValid() )
3064 {
3065 return QVariant( inter.hours() );
3066 }
3067 else
3068 {
3069 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3070 return QVariant( t1.hour() );
3071 }
3072}
3073
3074static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3075{
3076 QVariant value = values.at( 0 );
3077 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3078 if ( inter.isValid() )
3079 {
3080 return QVariant( inter.minutes() );
3081 }
3082 else
3083 {
3084 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3085 return QVariant( t1.minute() );
3086 }
3087}
3088
3089static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3090{
3091 QVariant value = values.at( 0 );
3092 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3093 if ( inter.isValid() )
3094 {
3095 return QVariant( inter.seconds() );
3096 }
3097 else
3098 {
3099 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3100 return QVariant( t1.second() );
3101 }
3102}
3103
3104static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3105{
3106 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3107 if ( dt.isValid() )
3108 {
3109 return QVariant( dt.toMSecsSinceEpoch() );
3110 }
3111 else
3112 {
3113 return QVariant();
3114 }
3115}
3116
3117static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3118{
3119 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3120 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3121 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3122}
3123
3124static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3125{
3126 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3127 if ( parent->hasEvalError() )
3128 {
3129 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3130 return QVariant();
3131 }
3132 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3133 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3134}
3135
3136static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3138 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3139 if ( parent->hasEvalError() )
3140 {
3141 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3142 return QVariant();
3143 }
3144 bool ok;
3145 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3146}
3147
3148double qDateTimeToDecimalYear( const QDateTime &dateTime )
3149{
3150 if ( !dateTime.isValid() )
3151 {
3152 return 0.0;
3153 }
3154
3155 const int year = dateTime.date().year();
3156 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3157 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3158 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3159 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3160 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3161}
3162
3163static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3164{
3165 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3166 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3167 if ( parent->hasEvalError() )
3168 {
3169 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3170 return QVariant();
3171 }
3172 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3173 if ( parent->hasEvalError() )
3174 {
3175 return QVariant();
3176 }
3177 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3178 if ( parent->hasEvalError() )
3179 {
3180 return QVariant();
3181 }
3182 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3183 if ( parent->hasEvalError() )
3184 {
3185 return QVariant();
3186 }
3187 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3188
3189 const QgsMagneticModel model( name, filePath );
3190 try
3191 {
3192 double declination = 0;
3193 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3194 {
3195 return declination;
3196 }
3197 else
3198 {
3199 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3200 }
3201 }
3202 catch ( QgsNotSupportedException &e )
3203 {
3204 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3205 }
3206 return QVariant();
3207}
3208
3209static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3210{
3211 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3212 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3213 if ( parent->hasEvalError() )
3214 {
3215 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3216 return QVariant();
3217 }
3218 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3219 if ( parent->hasEvalError() )
3220 {
3221 return QVariant();
3222 }
3223 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3224 if ( parent->hasEvalError() )
3225 {
3226 return QVariant();
3227 }
3228 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3229 if ( parent->hasEvalError() )
3230 {
3231 return QVariant();
3232 }
3233 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3234
3235 const QgsMagneticModel model( name, filePath );
3236 try
3237 {
3238 double inclination = 0;
3239 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3240 {
3241 return inclination;
3242 }
3243 else
3244 {
3245 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3246 }
3247 }
3248 catch ( QgsNotSupportedException &e )
3249 {
3250 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3251 }
3252 return QVariant();
3253}
3254
3255static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3256{
3257 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3258 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3259 if ( parent->hasEvalError() )
3260 {
3261 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3262 return QVariant();
3263 }
3264 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3265 if ( parent->hasEvalError() )
3266 {
3267 return QVariant();
3268 }
3269 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3270 if ( parent->hasEvalError() )
3271 {
3272 return QVariant();
3273 }
3274 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3275 if ( parent->hasEvalError() )
3276 {
3277 return QVariant();
3278 }
3279 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3280
3281 const QgsMagneticModel model( name, filePath );
3282 try
3283 {
3284 double declination = 0;
3285 double Bx = 0;
3286 double By = 0;
3287 double Bz = 0;
3288 double Bxt = 0;
3289 double Byt = 0;
3290 double Bzt = 0;
3291
3292 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3293 {
3294 double H = 0;
3295 double F = 0;
3296 double D = 0;
3297 double I = 0;
3298 double Ht = 0;
3299 double Ft = 0;
3300 double Dt = 0;
3301 double It = 0;
3302 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3303 {
3304 return Dt;
3305 }
3306 else
3307 {
3308 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3309 }
3310 return declination;
3311 }
3312 else
3313 {
3314 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3315 }
3316 }
3317 catch ( QgsNotSupportedException &e )
3318 {
3319 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3320 }
3321 return QVariant();
3322}
3323
3324static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3325{
3326 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3327 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3328 if ( parent->hasEvalError() )
3329 {
3330 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3331 return QVariant();
3332 }
3333 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3334 if ( parent->hasEvalError() )
3335 {
3336 return QVariant();
3337 }
3338 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3339 if ( parent->hasEvalError() )
3340 {
3341 return QVariant();
3342 }
3343 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3344 if ( parent->hasEvalError() )
3345 {
3346 return QVariant();
3347 }
3348 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3349
3350 const QgsMagneticModel model( name, filePath );
3351 try
3352 {
3353 double declination = 0;
3354 double Bx = 0;
3355 double By = 0;
3356 double Bz = 0;
3357 double Bxt = 0;
3358 double Byt = 0;
3359 double Bzt = 0;
3360
3361 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3362 {
3363 double H = 0;
3364 double F = 0;
3365 double D = 0;
3366 double I = 0;
3367 double Ht = 0;
3368 double Ft = 0;
3369 double Dt = 0;
3370 double It = 0;
3371 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3372 {
3373 return It;
3374 }
3375 else
3376 {
3377 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3378 }
3379 return declination;
3380 }
3381 else
3383 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3384 }
3385 }
3386 catch ( QgsNotSupportedException &e )
3387 {
3388 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3389 }
3390 return QVariant();
3391}
3392
3393#define ENSURE_GEOM_TYPE(f, g, geomtype) \
3394 if ( !(f).hasGeometry() ) \
3395 return QVariant(); \
3396 QgsGeometry g = (f).geometry(); \
3397 if ( (g).type() != (geomtype) ) \
3398 return QVariant();
3399
3400static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3401{
3402 FEAT_FROM_CONTEXT( context, f )
3404 if ( g.isMultipart() )
3405 {
3406 return g.asMultiPoint().at( 0 ).x();
3407 }
3408 else
3409 {
3410 return g.asPoint().x();
3411 }
3412}
3413
3414static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3415{
3416 FEAT_FROM_CONTEXT( context, f )
3418 if ( g.isMultipart() )
3419 {
3420 return g.asMultiPoint().at( 0 ).y();
3421 }
3422 else
3423 {
3424 return g.asPoint().y();
3425 }
3426}
3427
3428static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3429{
3430 FEAT_FROM_CONTEXT( context, f )
3432
3433 if ( g.isEmpty() )
3434 return QVariant();
3435
3436 const QgsAbstractGeometry *abGeom = g.constGet();
3437
3438 if ( g.isEmpty() || !abGeom->is3D() )
3439 return QVariant();
3440
3441 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3442 {
3443 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3444 if ( point )
3445 return point->z();
3446 }
3447 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3448 {
3449 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3450 {
3451 if ( collection->numGeometries() > 0 )
3452 {
3453 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3454 return point->z();
3455 }
3456 }
3457 }
3458
3459 return QVariant();
3460}
3461
3462static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3463{
3464 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3465 if ( geom.isNull() )
3466 return QVariant();
3467
3468 bool isValid = geom.isGeosValid();
3469
3470 return QVariant( isValid );
3471}
3472
3473static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3474{
3475 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3476 if ( geom.isNull() )
3477 return QVariant();
3478
3479 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3480#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3482#else
3484#endif
3485 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3487 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3489
3490 const bool keepCollapsed = values.value( 2 ).toBool();
3491
3492 QgsGeometry valid;
3493 try
3494 {
3495 valid = geom.makeValid( method, keepCollapsed );
3496 }
3497 catch ( QgsNotSupportedException & )
3498 {
3499 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3500 return QVariant();
3501 }
3502
3503 return QVariant::fromValue( valid );
3504}
3505
3506static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3507{
3508 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3509 if ( geom.isNull() )
3510 return QVariant();
3511
3512 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3513 QVariantList array;
3514 for ( int i = 0; i < multiGeom.size(); ++i )
3515 {
3516 array += QVariant::fromValue( multiGeom.at( i ) );
3517 }
3518
3519 return array;
3520}
3521
3522static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3523{
3524 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3525 if ( geom.isNull() )
3526 return QVariant();
3527
3528 //if single point, return the point's x coordinate
3529 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3530 {
3531 return geom.asPoint().x();
3532 }
3533
3534 //otherwise return centroid x
3535 QgsGeometry centroid = geom.centroid();
3536 QVariant result( centroid.asPoint().x() );
3537 return result;
3538}
3539
3540static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3541{
3542 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3543 if ( geom.isNull() )
3544 return QVariant();
3545
3546 //if single point, return the point's y coordinate
3547 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3548 {
3549 return geom.asPoint().y();
3550 }
3551
3552 //otherwise return centroid y
3553 QgsGeometry centroid = geom.centroid();
3554 QVariant result( centroid.asPoint().y() );
3555 return result;
3556}
3557
3558static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3559{
3560 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3561 if ( geom.isNull() )
3562 return QVariant(); //or 0?
3563
3564 if ( !geom.constGet()->is3D() )
3565 return QVariant();
3566
3567 //if single point, return the point's z coordinate
3568 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3569 {
3571 if ( point )
3572 return point->z();
3573 }
3574 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3575 {
3577 {
3578 if ( collection->numGeometries() == 1 )
3579 {
3580 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3581 return point->z();
3582 }
3583 }
3584 }
3585
3586 return QVariant();
3587}
3588
3589static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3590{
3591 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3592 if ( geom.isNull() )
3593 return QVariant(); //or 0?
3594
3595 if ( !geom.constGet()->isMeasure() )
3596 return QVariant();
3597
3598 //if single point, return the point's m value
3599 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3600 {
3602 if ( point )
3603 return point->m();
3604 }
3605 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3606 {
3608 {
3609 if ( collection->numGeometries() == 1 )
3610 {
3611 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3612 return point->m();
3613 }
3614 }
3615 }
3616
3617 return QVariant();
3618}
3619
3620static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3621{
3622 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3623
3624 if ( geom.isNull() )
3625 return QVariant();
3626
3627 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3628
3629 if ( idx < 0 )
3630 {
3631 //negative idx
3632 int count = geom.constGet()->nCoordinates();
3633 idx = count + idx;
3634 }
3635 else
3636 {
3637 //positive idx is 1 based
3638 idx -= 1;
3639 }
3640
3641 QgsVertexId vId;
3642 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3643 {
3644 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3645 return QVariant();
3646 }
3647
3648 QgsPoint point = geom.constGet()->vertexAt( vId );
3649 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3650}
3651
3652static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3653{
3654 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3655
3656 if ( geom.isNull() )
3657 return QVariant();
3658
3659 QgsVertexId vId;
3660 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3661 {
3662 return QVariant();
3663 }
3664
3665 QgsPoint point = geom.constGet()->vertexAt( vId );
3666 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3667}
3668
3669static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3670{
3671 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3672
3673 if ( geom.isNull() )
3674 return QVariant();
3675
3676 QgsVertexId vId;
3677 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3678 {
3679 return QVariant();
3680 }
3681
3682 QgsPoint point = geom.constGet()->vertexAt( vId );
3683 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3684}
3685
3686static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3687{
3688 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3689
3690 if ( geom.isNull() )
3691 return QVariant();
3692
3693 bool ignoreClosing = false;
3694 if ( values.length() > 1 )
3695 {
3696 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3697 }
3698
3699 QgsMultiPoint *mp = new QgsMultiPoint();
3700
3701 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3702 for ( const QgsRingSequence &part : sequence )
3703 {
3704 for ( const QgsPointSequence &ring : part )
3705 {
3706 bool skipLast = false;
3707 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3708 {
3709 skipLast = true;
3710 }
3711
3712 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3713 {
3714 mp->addGeometry( ring.at( i ).clone() );
3715 }
3716 }
3717 }
3718
3719 return QVariant::fromValue( QgsGeometry( mp ) );
3720}
3721
3722static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3723{
3724 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3725
3726 if ( geom.isNull() )
3727 return QVariant();
3728
3729 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3730
3731 //OK, now we have a complete list of segmentized lines from the geometry
3733 for ( QgsLineString *line : linesToProcess )
3734 {
3735 for ( int i = 0; i < line->numPoints() - 1; ++i )
3736 {
3738 segment->setPoints( QgsPointSequence()
3739 << line->pointN( i )
3740 << line->pointN( i + 1 ) );
3741 ml->addGeometry( segment );
3742 }
3743 delete line;
3744 }
3745
3746 return QVariant::fromValue( QgsGeometry( ml ) );
3747}
3748
3749static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3750{
3751 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3752
3753 if ( geom.isNull() )
3754 return QVariant();
3755
3757 if ( !curvePolygon && geom.isMultipart() )
3758 {
3760 {
3761 if ( collection->numGeometries() == 1 )
3762 {
3763 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3764 }
3765 }
3766 }
3767
3768 if ( !curvePolygon )
3769 return QVariant();
3770
3771 //idx is 1 based
3772 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3773
3774 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3775 return QVariant();
3776
3777 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3778 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3779 return result;
3780}
3781
3782static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3783{
3784 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3785
3786 if ( geom.isNull() )
3787 return QVariant();
3788
3790 if ( !collection )
3791 return QVariant();
3792
3793 //idx is 1 based
3794 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3795
3796 if ( idx < 0 || idx >= collection->numGeometries() )
3797 return QVariant();
3798
3799 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3800 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3801 return result;
3802}
3803
3804static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3805{
3806 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3807
3808 if ( geom.isNull() )
3809 return QVariant();
3810
3811 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3812 if ( !boundary )
3813 return QVariant();
3814
3815 return QVariant::fromValue( QgsGeometry( boundary ) );
3816}
3817
3818static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3819{
3820 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3821
3822 if ( geom.isNull() )
3823 return QVariant();
3824
3825 QgsGeometry merged = geom.mergeLines();
3826 if ( merged.isNull() )
3827 return QVariant();
3828
3829 return QVariant::fromValue( merged );
3830}
3831
3832static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3833{
3834 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3835 if ( geom.isNull() )
3836 return QVariant();
3837
3838 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3839 if ( geom2.isNull() )
3840 return QVariant();
3841
3842 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3843 if ( sharedPaths.isNull() )
3844 return QVariant();
3845
3846 return QVariant::fromValue( sharedPaths );
3847}
3848
3849
3850static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3851{
3852 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3853
3854 if ( geom.isNull() )
3855 return QVariant();
3856
3857 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3858
3859 QgsGeometry simplified = geom.simplify( tolerance );
3860 if ( simplified.isNull() )
3861 return QVariant();
3862
3863 return simplified;
3864}
3865
3866static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3867{
3868 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3869
3870 if ( geom.isNull() )
3871 return QVariant();
3872
3873 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3874
3876
3877 QgsGeometry simplified = simplifier.simplify( geom );
3878 if ( simplified.isNull() )
3879 return QVariant();
3880
3881 return simplified;
3882}
3883
3884static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3885{
3886 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3887
3888 if ( geom.isNull() )
3889 return QVariant();
3890
3891 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3892 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3893 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3894 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3895
3896 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3897 if ( smoothed.isNull() )
3898 return QVariant();
3899
3900 return smoothed;
3901}
3902
3903static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3904{
3905 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3906
3907 if ( geom.isNull() )
3908 return QVariant();
3909
3910 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3911 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3912 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3913
3914 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3915 if ( waved.isNull() )
3916 return QVariant();
3917
3918 return waved;
3919}
3920
3921static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3922{
3923 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3924
3925 if ( geom.isNull() )
3926 return QVariant();
3927
3928 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3929 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3930 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3931 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3932 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3933
3934 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3935 minAmplitude, maxAmplitude, seed );
3936 if ( waved.isNull() )
3937 return QVariant();
3938
3939 return waved;
3940}
3941
3942static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3943{
3944 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3945
3946 if ( geom.isNull() )
3947 return QVariant();
3948
3949 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3950 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3951 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3952
3953 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3954 if ( waved.isNull() )
3955 return QVariant();
3956
3957 return waved;
3958}
3959
3960static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3961{
3962 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3963
3964 if ( geom.isNull() )
3965 return QVariant();
3966
3967 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3968 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3969 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3970 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3971 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3972
3973 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3974 minAmplitude, maxAmplitude, seed );
3975 if ( waved.isNull() )
3976 return QVariant();
3977
3978 return waved;
3979}
3980
3981static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3982{
3983 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3984
3985 if ( geom.isNull() )
3986 return QVariant();
3987
3988 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3989 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3990 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3991
3992 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3993 if ( waved.isNull() )
3994 return QVariant();
3995
3996 return waved;
3997}
3998
3999static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4000{
4001 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4002
4003 if ( geom.isNull() )
4004 return QVariant();
4005
4006 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4007 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4008 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4009 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4010 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4011
4012 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
4013 minAmplitude, maxAmplitude, seed );
4014 if ( waved.isNull() )
4015 return QVariant();
4016
4017 return waved;
4018}
4019
4020static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4021{
4022 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4023
4024 if ( geom.isNull() )
4025 return QVariant();
4026
4027 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4028 QVector< double > dashPattern;
4029 dashPattern.reserve( pattern.size() );
4030 for ( const QVariant &value : std::as_const( pattern ) )
4031 {
4032 bool ok = false;
4033 double v = value.toDouble( &ok );
4034 if ( ok )
4035 {
4036 dashPattern << v;
4037 }
4038 else
4039 {
4040 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4041 return QgsGeometry();
4042 }
4043 }
4044
4045 if ( dashPattern.size() % 2 != 0 )
4046 {
4047 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4048 return QgsGeometry();
4049 }
4050
4051 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4053 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4055 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4057 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4059 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4061 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4063 else
4064 {
4065 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4066 return QgsGeometry();
4067 }
4068
4069 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4071 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4073 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4075 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4077 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4079 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4081 else
4082 {
4083 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4084 return QgsGeometry();
4085 }
4086
4087 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4089 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4091 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4093 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4095 else
4096 {
4097 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4098 return QgsGeometry();
4099 }
4100
4101 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4102
4103 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4104 if ( result.isNull() )
4105 return QVariant();
4106
4107 return result;
4108}
4109
4110static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4111{
4112 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4113
4114 if ( geom.isNull() )
4115 return QVariant();
4116
4117 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4118 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4119 if ( densified.isNull() )
4120 return QVariant();
4121
4122 return densified;
4123}
4124
4125static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4126{
4127 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4128
4129 if ( geom.isNull() )
4130 return QVariant();
4131
4132 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4133 const QgsGeometry densified = geom.densifyByDistance( distance );
4134 if ( densified.isNull() )
4135 return QVariant();
4136
4137 return densified;
4138}
4139
4140static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4141{
4142 QVariantList list;
4143 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4144 {
4145 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4146 }
4147 else
4148 {
4149 list = values;
4150 }
4151
4152 QVector< QgsGeometry > parts;
4153 parts.reserve( list.size() );
4154 for ( const QVariant &value : std::as_const( list ) )
4155 {
4156 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4157 if ( part.isNull() )
4158 return QgsGeometry();
4159 parts << part;
4160 }
4161
4162 return QgsGeometry::collectGeometry( parts );
4163}
4164
4165static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4166{
4167 if ( values.count() < 2 || values.count() > 4 )
4168 {
4169 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4170 return QVariant();
4171 }
4172
4173 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4174 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4175 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4176 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4177 switch ( values.count() )
4178 {
4179 case 2:
4180 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4181 case 3:
4182 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4183 case 4:
4184 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4185 }
4186 return QVariant(); //avoid warning
4187}
4188
4189static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4190{
4191 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4192 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4193 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4194 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4195}
4196
4197static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4198{
4199 if ( values.empty() )
4200 {
4201 return QVariant();
4202 }
4203
4204 QVector<QgsPoint> points;
4205 points.reserve( values.count() );
4206
4207 auto addPoint = [&points]( const QgsGeometry & geom )
4208 {
4209 if ( geom.isNull() )
4210 return;
4211
4212 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4213 return;
4214
4216 if ( !point )
4217 return;
4218
4219 points << *point;
4220 };
4221
4222 for ( const QVariant &value : values )
4223 {
4224 if ( value.userType() == QMetaType::Type::QVariantList )
4225 {
4226 const QVariantList list = value.toList();
4227 for ( const QVariant &v : list )
4228 {
4229 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4230 }
4231 }
4232 else
4233 {
4234 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4235 }
4236 }
4237
4238 if ( points.count() < 2 )
4239 return QVariant();
4240
4241 return QgsGeometry( new QgsLineString( points ) );
4242}
4243
4244static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4245{
4246 if ( values.count() < 1 )
4247 {
4248 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4249 return QVariant();
4250 }
4251
4252 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4253
4254 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4255 return outerRing; // if it's already a polygon we have nothing to do
4256
4257 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4258 return QVariant();
4259
4260 auto polygon = std::make_unique< QgsPolygon >();
4261
4262 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4263 if ( !exteriorRing && outerRing.isMultipart() )
4264 {
4266 {
4267 if ( collection->numGeometries() == 1 )
4268 {
4269 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4270 }
4271 }
4272 }
4273
4274 if ( !exteriorRing )
4275 return QVariant();
4276
4277 polygon->setExteriorRing( exteriorRing->segmentize() );
4278
4279
4280 for ( int i = 1; i < values.count(); ++i )
4281 {
4282 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4283 if ( ringGeom.isNull() )
4284 continue;
4285
4286 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4287 continue;
4288
4289 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4290 if ( !ring && ringGeom.isMultipart() )
4291 {
4293 {
4294 if ( collection->numGeometries() == 1 )
4295 {
4296 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4297 }
4298 }
4299 }
4300
4301 if ( !ring )
4302 continue;
4303
4304 polygon->addInteriorRing( ring->segmentize() );
4305 }
4306
4307 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4308}
4309
4310static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4311{
4312 auto tr = std::make_unique<QgsTriangle>();
4313 auto lineString = std::make_unique<QgsLineString>();
4314 lineString->clear();
4315
4316 for ( const QVariant &value : values )
4317 {
4318 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4319 if ( geom.isNull() )
4320 return QVariant();
4321
4322 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4323 return QVariant();
4324
4326 if ( !point && geom.isMultipart() )
4327 {
4329 {
4330 if ( collection->numGeometries() == 1 )
4331 {
4332 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4333 }
4334 }
4335 }
4336
4337 if ( !point )
4338 return QVariant();
4339
4340 lineString->addVertex( *point );
4341 }
4342
4343 tr->setExteriorRing( lineString.release() );
4344
4345 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4346}
4347
4348static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4349{
4350 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4351 if ( geom.isNull() )
4352 return QVariant();
4353
4354 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4355 return QVariant();
4356
4357 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4358 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4359
4360 if ( segment < 3 )
4361 {
4362 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4363 return QVariant();
4364 }
4366 if ( !point && geom.isMultipart() )
4367 {
4369 {
4370 if ( collection->numGeometries() == 1 )
4371 {
4372 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4373 }
4374 }
4375 }
4376 if ( !point )
4377 return QVariant();
4378
4379 QgsCircle circ( *point, radius );
4380 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4381}
4382
4383static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4384{
4385 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4386 if ( geom.isNull() )
4387 return QVariant();
4388
4389 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4390 return QVariant();
4391
4392 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4393 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4394 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4395 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4396 if ( segment < 3 )
4397 {
4398 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4399 return QVariant();
4400 }
4402 if ( !point && geom.isMultipart() )
4403 {
4405 {
4406 if ( collection->numGeometries() == 1 )
4407 {
4408 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4409 }
4410 }
4411 }
4412 if ( !point )
4413 return QVariant();
4414
4415 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4416 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4417}
4418
4419static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4420{
4421
4422 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4423 if ( pt1.isNull() )
4424 return QVariant();
4425
4426 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4427 return QVariant();
4428
4429 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4430 if ( pt2.isNull() )
4431 return QVariant();
4432
4433 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4434 return QVariant();
4435
4436 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4437 if ( nbEdges < 3 )
4438 {
4439 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4440 return QVariant();
4441 }
4442
4443 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4445 {
4446 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4447 return QVariant();
4448 }
4449
4451 if ( !center && pt1.isMultipart() )
4452 {
4454 {
4455 if ( collection->numGeometries() == 1 )
4456 {
4457 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4458 }
4459 }
4460 }
4461 if ( !center )
4462 return QVariant();
4463
4465 if ( !corner && pt2.isMultipart() )
4466 {
4468 {
4469 if ( collection->numGeometries() == 1 )
4470 {
4471 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4472 }
4473 }
4474 }
4475 if ( !corner )
4476 return QVariant();
4477
4478 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4479
4480 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4481
4482}
4483
4484static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4485{
4486 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4487 if ( pt1.isNull() )
4488 return QVariant();
4489 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4490 return QVariant();
4491
4492 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4493 if ( pt2.isNull() )
4494 return QVariant();
4495 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4496 return QVariant();
4497
4498 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4499 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4500 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4501
4502 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4503}
4504
4505static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4506{
4507 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4508 if ( pt1.isNull() )
4509 return QVariant();
4510 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4511 return QVariant();
4512
4513 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4514 if ( pt2.isNull() )
4515 return QVariant();
4516 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4517 return QVariant();
4518
4519 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4520 if ( pt3.isNull() )
4521 return QVariant();
4522 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4523 return QVariant();
4524
4525 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4526 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4527 {
4528 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4529 return QVariant();
4530 }
4531 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4532 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4533 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4534 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4535 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4536}
4537
4538static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4539{
4540 if ( geom.isNull() )
4541 return QVariant();
4542
4543 if ( idx < 0 )
4544 {
4545 idx += geom.constGet()->nCoordinates();
4546 }
4547 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4548 {
4549 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4550 return QVariant();
4551 }
4552 return QVariant::fromValue( geom.vertexAt( idx ) );
4553}
4554
4555// function used for the old $ style
4556static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4557{
4558 FEAT_FROM_CONTEXT( context, feature )
4559 const QgsGeometry geom = feature.geometry();
4560 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4561
4562 const QVariant v = pointAt( geom, idx, parent );
4563
4564 if ( !v.isNull() )
4565 return QVariant( v.value<QgsPoint>().x() );
4566 else
4567 return QVariant();
4568}
4569static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4570{
4571 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))
4572 {
4573 return fcnOldXat( values, f, parent, node );
4574 }
4575 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)
4576 {
4577 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4578 }
4579
4580 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4581 if ( geom.isNull() )
4582 {
4583 return QVariant();
4584 }
4585
4586 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4587
4588 const QVariant v = pointAt( geom, vertexNumber, parent );
4589 if ( !v.isNull() )
4590 return QVariant( v.value<QgsPoint>().x() );
4591 else
4592 return QVariant();
4593}
4594
4595// function used for the old $ style
4596static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4597{
4598 FEAT_FROM_CONTEXT( context, feature )
4599 const QgsGeometry geom = feature.geometry();
4600 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4601
4602 const QVariant v = pointAt( geom, idx, parent );
4603
4604 if ( !v.isNull() )
4605 return QVariant( v.value<QgsPoint>().y() );
4606 else
4607 return QVariant();
4608}
4609static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4610{
4611 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))
4612 {
4613 return fcnOldYat( values, f, parent, node );
4614 }
4615 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)
4616 {
4617 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4618 }
4619
4620 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4621 if ( geom.isNull() )
4622 {
4623 return QVariant();
4624 }
4625
4626 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4627
4628 const QVariant v = pointAt( geom, vertexNumber, parent );
4629 if ( !v.isNull() )
4630 return QVariant( v.value<QgsPoint>().y() );
4631 else
4632 return QVariant();
4633}
4634
4635static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4636{
4637 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4638 if ( geom.isNull() )
4639 {
4640 return QVariant();
4641 }
4642
4643 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4644
4645 const QVariant v = pointAt( geom, vertexNumber, parent );
4646 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4647 return QVariant( v.value<QgsPoint>().z() );
4648 else
4649 return QVariant();
4650}
4651
4652static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4653{
4654 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4655 if ( geom.isNull() )
4656 {
4657 return QVariant();
4658 }
4659
4660 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4661
4662 const QVariant v = pointAt( geom, vertexNumber, parent );
4663 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4664 return QVariant( v.value<QgsPoint>().m() );
4665 else
4666 return QVariant();
4667}
4668
4669
4670static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4671{
4672 if ( !context )
4673 return QVariant();
4674
4675 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4676 if ( context->hasGeometry() )
4677 return context->geometry();
4678 else
4679 {
4680 FEAT_FROM_CONTEXT( context, f )
4681 QgsGeometry geom = f.geometry();
4682 if ( !geom.isNull() )
4683 return QVariant::fromValue( geom );
4684 else
4685 return QVariant();
4686 }
4687}
4688
4689static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4690{
4691 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4692 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4693 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4694 return result;
4695}
4696
4697static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4698{
4699 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4700 if ( wkb.isNull() )
4701 return QVariant();
4702
4703 QgsGeometry geom;
4704 geom.fromWkb( wkb );
4705 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4706}
4707
4708static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4709{
4710 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4711 QgsOgcUtils::Context ogcContext;
4712 if ( context )
4713 {
4714 QgsWeakMapLayerPointer mapLayerPtr {context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4715 if ( mapLayerPtr )
4716 {
4717 ogcContext.layer = mapLayerPtr.data();
4718 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4719 }
4720 }
4721 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4722 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4723 return result;
4724}
4725
4726static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4727{
4728 FEAT_FROM_CONTEXT( context, f )
4730 QgsDistanceArea *calc = parent->geomCalculator();
4731 if ( calc )
4732 {
4733 try
4734 {
4735 double area = calc->measureArea( f.geometry() );
4736 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4737 return QVariant( area );
4738 }
4739 catch ( QgsCsException & )
4740 {
4741 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4742 return QVariant();
4743 }
4744 }
4745 else
4746 {
4747 return QVariant( f.geometry().area() );
4748 }
4749}
4750
4751static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4752{
4753 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4754
4755 if ( geom.type() != Qgis::GeometryType::Polygon )
4756 return QVariant();
4757
4758 return QVariant( geom.area() );
4759}
4760
4761static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4762{
4763 FEAT_FROM_CONTEXT( context, f )
4765 QgsDistanceArea *calc = parent->geomCalculator();
4766 if ( calc )
4767 {
4768 try
4769 {
4770 double len = calc->measureLength( f.geometry() );
4771 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4772 return QVariant( len );
4773 }
4774 catch ( QgsCsException & )
4775 {
4776 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4777 return QVariant();
4778 }
4779 }
4780 else
4781 {
4782 return QVariant( f.geometry().length() );
4783 }
4784}
4785
4786static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4787{
4788 FEAT_FROM_CONTEXT( context, f )
4790 QgsDistanceArea *calc = parent->geomCalculator();
4791 if ( calc )
4792 {
4793 try
4794 {
4795 double len = calc->measurePerimeter( f.geometry() );
4796 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4797 return QVariant( len );
4798 }
4799 catch ( QgsCsException & )
4800 {
4801 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4802 return QVariant();
4803 }
4804 }
4805 else
4806 {
4807 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4808 }
4809}
4810
4811static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4812{
4813 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4814
4815 if ( geom.type() != Qgis::GeometryType::Polygon )
4816 return QVariant();
4817
4818 //length for polygons = perimeter
4819 return QVariant( geom.length() );
4820}
4821
4822static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4823{
4824 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4825 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4826}
4827
4828static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4829{
4830 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4831 if ( geom.isNull() )
4832 return QVariant();
4833
4834 return QVariant( geom.constGet()->partCount() );
4835}
4836
4837static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4838{
4839 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4840 if ( geom.isNull() )
4841 return QVariant();
4842
4843 return QVariant( geom.isMultipart() );
4844}
4845
4846static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4847{
4848 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4849
4850 if ( geom.isNull() )
4851 return QVariant();
4852
4854 if ( curvePolygon )
4855 return QVariant( curvePolygon->numInteriorRings() );
4856
4858 if ( collection )
4859 {
4860 //find first CurvePolygon in collection
4861 for ( int i = 0; i < collection->numGeometries(); ++i )
4862 {
4863 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4864 if ( !curvePolygon )
4865 continue;
4866
4867 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4868 }
4869 }
4870
4871 return QVariant();
4872}
4873
4874static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4875{
4876 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4877
4878 if ( geom.isNull() )
4879 return QVariant();
4880
4882 if ( curvePolygon )
4883 return QVariant( curvePolygon->ringCount() );
4884
4885 bool foundPoly = false;
4886 int ringCount = 0;
4888 if ( collection )
4889 {
4890 //find CurvePolygons in collection
4891 for ( int i = 0; i < collection->numGeometries(); ++i )
4892 {
4893 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4894 if ( !curvePolygon )
4895 continue;
4896
4897 foundPoly = true;
4898 ringCount += curvePolygon->ringCount();
4899 }
4900 }
4901
4902 if ( !foundPoly )
4903 return QVariant();
4904
4905 return QVariant( ringCount );
4906}
4907
4908static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4909{
4910 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4911 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4912 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4913 return result;
4914}
4915
4916static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4917{
4918 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4919 return QVariant::fromValue( geom.boundingBox().width() );
4920}
4921
4922static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4923{
4924 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4925 return QVariant::fromValue( geom.boundingBox().height() );
4926}
4927
4928static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4929{
4930 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4931 if ( geom.isNull() )
4932 return QVariant();
4933
4935}
4936
4937static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4938{
4939 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4940 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4941}
4942
4943static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4944{
4945 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4946 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4947}
4948
4949static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4950{
4951 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4952 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4953}
4954
4955static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4956{
4957 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4958 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4959}
4960
4961static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4962{
4963 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4964
4965 if ( geom.isNull() || geom.isEmpty( ) )
4966 return QVariant();
4967
4968 if ( !geom.constGet()->is3D() )
4969 return QVariant();
4970
4971 double max = std::numeric_limits< double >::lowest();
4972
4973 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4974 {
4975 double z = ( *it ).z();
4976
4977 if ( max < z )
4978 max = z;
4979 }
4980
4981 if ( max == std::numeric_limits< double >::lowest() )
4982 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4983
4984 return QVariant( max );
4985}
4986
4987static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4988{
4989 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4990
4991 if ( geom.isNull() || geom.isEmpty() )
4992 return QVariant();
4993
4994 if ( !geom.constGet()->is3D() )
4995 return QVariant();
4996
4997 double min = std::numeric_limits< double >::max();
4998
4999 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5000 {
5001 double z = ( *it ).z();
5002
5003 if ( z < min )
5004 min = z;
5005 }
5006
5007 if ( min == std::numeric_limits< double >::max() )
5008 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5009
5010 return QVariant( min );
5011}
5012
5013static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5014{
5015 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5016
5017 if ( geom.isNull() || geom.isEmpty() )
5018 return QVariant();
5019
5020 if ( !geom.constGet()->isMeasure() )
5021 return QVariant();
5022
5023 double min = std::numeric_limits< double >::max();
5024
5025 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5026 {
5027 double m = ( *it ).m();
5028
5029 if ( m < min )
5030 min = m;
5031 }
5032
5033 if ( min == std::numeric_limits< double >::max() )
5034 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5035
5036 return QVariant( min );
5037}
5038
5039static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5040{
5041 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5042
5043 if ( geom.isNull() || geom.isEmpty() )
5044 return QVariant();
5045
5046 if ( !geom.constGet()->isMeasure() )
5047 return QVariant();
5048
5049 double max = std::numeric_limits< double >::lowest();
5050
5051 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5052 {
5053 double m = ( *it ).m();
5054
5055 if ( max < m )
5056 max = m;
5057 }
5058
5059 if ( max == std::numeric_limits< double >::lowest() )
5060 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5061
5062 return QVariant( max );
5063}
5064
5065static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5066{
5067 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5069 if ( !curve )
5070 {
5071 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5072 return QVariant();
5073 }
5074
5075 return QVariant( curve->sinuosity() );
5076}
5077
5078static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5079{
5080 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5081 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5082 if ( !curve )
5083 {
5084 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5085 return QVariant();
5086 }
5087
5088 return QVariant( curve->straightDistance2d() );
5089}
5090
5091static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5092{
5093 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5095
5096 if ( !poly )
5097 {
5098 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5099 return QVariant();
5100 }
5101
5102 return QVariant( poly->roundness() );
5103}
5104
5105
5106
5107static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5108{
5109 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5110 if ( geom.isNull() )
5111 return QVariant();
5112
5113 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5114 flipped->swapXy();
5115 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5116}
5117
5118static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5119{
5120 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5121 if ( fGeom.isNull() )
5122 return QVariant();
5123
5124 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5125 if ( !curve && fGeom.isMultipart() )
5126 {
5128 {
5129 if ( collection->numGeometries() == 1 )
5130 {
5131 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5132 }
5133 }
5134 }
5135
5136 if ( !curve )
5137 return QVariant();
5138
5139 return QVariant::fromValue( curve->isClosed() );
5140}
5141
5142static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5143{
5144 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5145
5146 if ( geom.isNull() )
5147 return QVariant();
5148
5149 QVariant result;
5150 if ( !geom.isMultipart() )
5151 {
5153
5154 if ( !line )
5155 return QVariant();
5156
5157 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5158 closedLine->close();
5159
5160 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5161 }
5162 else
5163 {
5165 if ( !collection )
5166 return QVariant();
5167
5168 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5169
5170 for ( int i = 0; i < collection->numGeometries(); ++i )
5171 {
5172 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5173 {
5174 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5175 closedLine->close();
5176
5177 closed->addGeometry( closedLine.release() );
5178 }
5179 }
5180 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5181 }
5182
5183 return result;
5184}
5185
5186static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5187{
5188 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5189 if ( fGeom.isNull() )
5190 return QVariant();
5191
5192 return QVariant::fromValue( fGeom.isEmpty() );
5193}
5194
5195static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5196{
5197 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5198 return QVariant::fromValue( true );
5199
5200 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5201 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5202}
5203
5204static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5205{
5206 if ( values.length() < 2 || values.length() > 3 )
5207 return QVariant();
5208
5209 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5210 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5211
5212 if ( fGeom.isNull() || sGeom.isNull() )
5213 return QVariant();
5214
5215 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5216
5217 if ( values.length() == 2 )
5218 {
5219 //two geometry arguments, return relation
5220 QString result = engine->relate( sGeom.constGet() );
5221 return QVariant::fromValue( result );
5222 }
5223 else
5224 {
5225 //three arguments, test pattern
5226 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5227 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5228 return QVariant::fromValue( result );
5229 }
5230}
5231
5232static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5233{
5234 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5235 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5236 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5237}
5238static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5239{
5240 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5241 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5242 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5243}
5244static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5245{
5246 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5247 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5248 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5249}
5250static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5251{
5252 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5253 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5254 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5255}
5256static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5257{
5258 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5259 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5260 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5261}
5262static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5263{
5264 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5265 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5266 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5267}
5268static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5269{
5270 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5271 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5272 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5273}
5274static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5275{
5276 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5277 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5278 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5279}
5280
5281static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5282{
5283 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5284 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5285 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5286 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5287 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5288 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5289
5291 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5292 capStyle = Qgis::EndCapStyle::Flat;
5293 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5294 capStyle = Qgis::EndCapStyle::Square;
5295
5297 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5298 joinStyle = Qgis::JoinStyle::Miter;
5299 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5300 joinStyle = Qgis::JoinStyle::Bevel;
5301
5302 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5303 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5304 return result;
5305}
5306
5307static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5308{
5309 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5310 const QgsGeometry reoriented = fGeom.forceRHR();
5311 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5312}
5313
5314static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5315{
5316 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5317 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5318 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5319}
5320
5321static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5322{
5323 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5324 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5325 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5326}
5327
5328static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5329{
5330 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5332 if ( !pt && fGeom.isMultipart() )
5333 {
5335 {
5336 if ( collection->numGeometries() == 1 )
5337 {
5338 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5339 }
5340 }
5341 }
5342
5343 if ( !pt )
5344 {
5345 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5346 return QVariant();
5347 }
5348
5349 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5350 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5351 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5352 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5353
5354 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5355 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5356 return result;
5357}
5358
5359static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5360{
5361 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5362 if ( fGeom.type() != Qgis::GeometryType::Line )
5363 {
5364 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5365 return QVariant();
5366 }
5367
5368 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5369 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5370 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5371
5372 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5373 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5374 return result;
5375}
5376
5377static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5378{
5379 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5380 if ( fGeom.type() != Qgis::GeometryType::Line )
5381 {
5382 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5383 return QVariant();
5384 }
5385
5386 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5387
5388 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5389 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5390 return result;
5391}
5392
5393static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5394{
5395 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5396 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5397 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5398 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5399 if ( joinInt < 1 || joinInt > 3 )
5400 return QVariant();
5401 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5402
5403 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5404
5405 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5406 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5407 return result;
5408}
5409
5410static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5411{
5412 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5413 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5414 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5415
5416 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5417 if ( joinInt < 1 || joinInt > 3 )
5418 return QVariant();
5419 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5420
5421 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5422
5423 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5424 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5425 return result;
5426}
5427
5428static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5429{
5430 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5431 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5432 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5433
5434 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5435 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5436 return result;
5437}
5438
5439static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5440{
5441 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5442 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5443 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5444 fGeom.translate( dx, dy );
5445 return QVariant::fromValue( fGeom );
5446}
5447
5448static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5449{
5450 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5451 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5452 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5453 : QgsGeometry();
5454 const bool perPart = values.value( 3 ).toBool();
5455
5456 if ( center.isNull() && perPart && fGeom.isMultipart() )
5457 {
5458 // no explicit center, rotating per part
5459 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5460 // the result is equivalent to setting perPart as false anyway)
5461 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5462 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5463 {
5464 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5465 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5466 t.rotate( -rotation );
5467 t.translate( -partCenter.x(), -partCenter.y() );
5468 ( *it )->transform( t );
5469 }
5470 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5471 }
5472 else
5473 {
5474 QgsPointXY pt;
5475 if ( center.isEmpty() )
5476 {
5477 // if center wasn't specified, use bounding box centroid
5478 pt = fGeom.boundingBox().center();
5479 }
5481 {
5482 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5483 return QVariant();
5484 }
5485 else
5486 {
5488 }
5489
5490 fGeom.rotate( rotation, pt );
5491 return QVariant::fromValue( fGeom );
5492 }
5493}
5494
5495static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5496{
5497 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5498 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5499 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5500 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5501 : QgsGeometry();
5502
5503 QgsPointXY pt;
5504 if ( center.isNull() )
5505 {
5506 // if center wasn't specified, use bounding box centroid
5507 pt = fGeom.boundingBox().center();
5508 }
5510 {
5511 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5512 return QVariant();
5513 }
5514 else
5515 {
5516 pt = center.asPoint();
5517 }
5518
5519 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5520 t.scale( xScale, yScale );
5521 t.translate( -pt.x(), -pt.y() );
5522 fGeom.transform( t );
5523 return QVariant::fromValue( fGeom );
5524}
5525
5526static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5527{
5528 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5529 if ( fGeom.isNull() )
5530 {
5531 return QVariant();
5532 }
5533
5534 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5535 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5536
5537 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5538
5539 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5540 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5541
5542 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5543 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5544 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5545 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5546
5547 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5548 {
5549 fGeom.get()->addZValue( 0 );
5550 }
5551 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5552 {
5553 fGeom.get()->addMValue( 0 );
5554 }
5555
5556 QTransform transform;
5557 transform.translate( deltaX, deltaY );
5558 transform.rotate( rotationZ );
5559 transform.scale( scaleX, scaleY );
5560 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5561
5562 return QVariant::fromValue( fGeom );
5563}
5564
5565
5566static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5567{
5568 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5569 QgsGeometry geom = fGeom.centroid();
5570 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5571 return result;
5572}
5573static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5574{
5575 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5576 QgsGeometry geom = fGeom.pointOnSurface();
5577 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5578 return result;
5579}
5580
5581static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5582{
5583 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5584 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5585 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5586 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5587 return result;
5588}
5589
5590static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5591{
5592 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5593 QgsGeometry geom = fGeom.convexHull();
5594 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5595 return result;
5596}
5597
5598#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5599static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5600{
5601 try
5602 {
5603 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5604 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5605 const bool allowHoles = values.value( 2 ).toBool();
5606 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5607 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5608 return result;
5609 }
5610 catch ( QgsCsException &cse )
5611 {
5612 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5613 return QVariant();
5614 }
5615}
5616#endif
5617
5618static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5619{
5620 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5621 int segments = 36;
5622 if ( values.length() == 2 )
5623 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5624 if ( segments < 0 )
5625 {
5626 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5627 return QVariant();
5628 }
5629
5630 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5631 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5632 return result;
5633}
5634
5635static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5636{
5637 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5639 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5640 return result;
5641}
5642
5643static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5644{
5645 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5646
5647 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5648 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5649 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5650
5651 double area, angle, width, height;
5652 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5653
5654 if ( geom.isNull() )
5655 {
5656 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5657 return QVariant();
5658 }
5659 return angle;
5660}
5661
5662static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5663{
5664 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5665 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5666 QgsGeometry geom = fGeom.difference( sGeom );
5667 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5668 return result;
5669}
5670
5671static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5672{
5673 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5674 return QVariant();
5675
5676 // two variants, one for geometry, one for string
5677
5678 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5679 if ( !fGeom.isNull() )
5680 {
5681 QVariant result;
5682 if ( !fGeom.isMultipart() )
5683 {
5684 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5685 if ( !curve )
5686 return QVariant();
5687
5688 QgsCurve *reversed = curve->reversed();
5689 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5690 }
5691 else
5692 {
5694 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5695 for ( int i = 0; i < collection->numGeometries(); ++i )
5696 {
5697 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5698 {
5699 reversed->addGeometry( curve->reversed() );
5700 }
5701 else
5702 {
5703 reversed->addGeometry( collection->geometryN( i )->clone() );
5704 }
5705 }
5706 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5707 }
5708 return result;
5709 }
5710
5711 //fall back to string variant
5712 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5713 std::reverse( string.begin(), string.end() );
5714 return string;
5715}
5716
5717static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5718{
5719 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5720 if ( fGeom.isNull() )
5721 return QVariant();
5722
5724 if ( !curvePolygon && fGeom.isMultipart() )
5725 {
5727 {
5728 if ( collection->numGeometries() == 1 )
5729 {
5730 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5731 }
5732 }
5733 }
5734
5735 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5736 return QVariant();
5737
5738 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5739 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5740 return result;
5741}
5742
5743static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5744{
5745 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5746 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5747 return QVariant( fGeom.distance( sGeom ) );
5748}
5749
5750static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5751{
5752 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5753 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5754
5755 double res = -1;
5756 if ( values.length() == 3 && values.at( 2 ).isValid() )
5757 {
5758 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5759 densify = std::clamp( densify, 0.0, 1.0 );
5760 res = g1.hausdorffDistanceDensify( g2, densify );
5761 }
5762 else
5763 {
5764 res = g1.hausdorffDistance( g2 );
5765 }
5766
5767 return res > -1 ? QVariant( res ) : QVariant();
5768}
5769
5770static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5771{
5772 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5773 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5774 QgsGeometry geom = fGeom.intersection( sGeom );
5775 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5776 return result;
5777}
5778static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5779{
5780 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5781 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5782 QgsGeometry geom = fGeom.symDifference( sGeom );
5783 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5784 return result;
5785}
5786static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5787{
5788 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5789 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5790 QgsGeometry geom = fGeom.combine( sGeom );
5791 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5792 return result;
5793}
5794
5795static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5796{
5797 if ( values.length() < 1 || values.length() > 2 )
5798 return QVariant();
5799
5800 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5801 int prec = 8;
5802 if ( values.length() == 2 )
5803 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5804 QString wkt = fGeom.asWkt( prec );
5805 return QVariant( wkt );
5806}
5807
5808static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5809{
5810 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5811 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5812}
5813
5814static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5815{
5816 if ( values.length() != 2 )
5817 {
5818 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5819 return QVariant();
5820 }
5821
5822 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5823 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5824
5825 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5826 if ( !pt1 && fGeom1.isMultipart() )
5827 {
5829 {
5830 if ( collection->numGeometries() == 1 )
5831 {
5832 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5833 }
5834 }
5835 }
5836
5837 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5838 if ( !pt2 && fGeom2.isMultipart() )
5839 {
5841 {
5842 if ( collection->numGeometries() == 1 )
5843 {
5844 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5845 }
5846 }
5847 }
5848
5849 if ( !pt1 || !pt2 )
5850 {
5851 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5852 return QVariant();
5853 }
5854
5855 // Code from PostGIS
5856 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5857 {
5858 if ( pt1->y() < pt2->y() )
5859 return 0.0;
5860 else if ( pt1->y() > pt2->y() )
5861 return M_PI;
5862 else
5863 return 0;
5864 }
5865
5866 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5867 {
5868 if ( pt1->x() < pt2->x() )
5869 return M_PI_2;
5870 else if ( pt1->x() > pt2->x() )
5871 return M_PI + ( M_PI_2 );
5872 else
5873 return 0;
5874 }
5875
5876 if ( pt1->x() < pt2->x() )
5877 {
5878 if ( pt1->y() < pt2->y() )
5879 {
5880 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5881 }
5882 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5883 {
5884 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5885 + ( M_PI_2 );
5886 }
5887 }
5888
5889 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5890 {
5891 if ( pt1->y() > pt2->y() )
5892 {
5893 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5894 + M_PI;
5895 }
5896 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5897 {
5898 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5899 + ( M_PI + ( M_PI_2 ) );
5900 }
5901 }
5902}
5903
5904static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5905{
5906 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5907 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5908 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
5909 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5910
5911 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5912 {
5913 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5914 return QVariant();
5915 }
5916
5917 const QgsPointXY point1 = geom1.asPoint();
5918 const QgsPointXY point2 = geom2.asPoint();
5919 if ( point1.isEmpty() || point2.isEmpty() )
5920 {
5921 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5922 return QVariant();
5923 }
5924
5926 if ( context )
5927 {
5928 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
5929
5930 if ( !sourceCrs.isValid() )
5931 {
5932 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
5933 }
5934
5935 if ( ellipsoid.isEmpty() )
5936 {
5937 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
5938 }
5939 }
5940
5941 if ( !sourceCrs.isValid() )
5942 {
5943 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5944 return QVariant();
5945 }
5946
5947 QgsDistanceArea da;
5948 da.setSourceCrs( sourceCrs, tContext );
5949 if ( !da.setEllipsoid( ellipsoid ) )
5950 {
5951 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5952 return QVariant();
5953 }
5954
5955 try
5956 {
5957 const double bearing = da.bearing( point1, point2 );
5958 if ( std::isfinite( bearing ) )
5959 {
5960 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5961 }
5962 }
5963 catch ( QgsCsException &cse )
5964 {
5965 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5966 return QVariant();
5967 }
5968 return QVariant();
5969}
5970
5971static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5972{
5973 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5974
5976 {
5977 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
5978 return QVariant();
5979 }
5980
5981 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5982 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5983 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5984
5985 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5986 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5987
5988 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5989}
5990
5991static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5992{
5993 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5994 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5995
5996 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5997 if ( !pt1 && fGeom1.isMultipart() )
5998 {
6000 {
6001 if ( collection->numGeometries() == 1 )
6002 {
6003 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6004 }
6005 }
6006 }
6007 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6008 if ( !pt2 && fGeom2.isMultipart() )
6009 {
6011 {
6012 if ( collection->numGeometries() == 1 )
6013 {
6014 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6015 }
6016 }
6017 }
6018
6019 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
6020 !pt1 || !pt2 )
6021 {
6022 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6023 return QVariant();
6024 }
6025
6026 return pt1->inclination( *pt2 );
6027
6028}
6029
6030static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6031{
6032 if ( values.length() != 3 )
6033 return QVariant();
6034
6035 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6036 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6037 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6038
6039 QgsGeometry geom = fGeom.extrude( x, y );
6040
6041 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6042 return result;
6043}
6044
6045static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6046{
6047 if ( values.length() < 2 )
6048 return QVariant();
6049
6050 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6051
6052 if ( !fGeom.isMultipart() )
6053 return values.at( 0 );
6054
6055 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6056 QVariant cachedExpression;
6057 if ( ctx )
6058 cachedExpression = ctx->cachedValue( expString );
6059 QgsExpression expression;
6060
6061 if ( cachedExpression.isValid() )
6062 {
6063 expression = cachedExpression.value<QgsExpression>();
6064 }
6065 else
6066 expression = QgsExpression( expString );
6067
6068 bool asc = values.value( 2 ).toBool();
6069
6070 QgsExpressionContext *unconstedContext = nullptr;
6071 QgsFeature f;
6072 if ( ctx )
6073 {
6074 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6075 // so no reason to worry
6076 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6077 f = ctx->feature();
6078 }
6079 else
6080 {
6081 // If there's no context provided, create a fake one
6082 unconstedContext = new QgsExpressionContext();
6083 }
6084
6086 Q_ASSERT( collection ); // Should have failed the multipart check above
6087
6089 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6090 QgsExpressionSorter sorter( orderBy );
6091
6092 QList<QgsFeature> partFeatures;
6093 partFeatures.reserve( collection->partCount() );
6094 for ( int i = 0; i < collection->partCount(); ++i )
6095 {
6096 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6097 partFeatures << f;
6098 }
6099
6100 sorter.sortFeatures( partFeatures, unconstedContext );
6101
6103
6104 Q_ASSERT( orderedGeom );
6105
6106 while ( orderedGeom->partCount() )
6107 orderedGeom->removeGeometry( 0 );
6108
6109 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6110 {
6111 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6112 }
6113
6114 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6115
6116 if ( !ctx )
6117 delete unconstedContext;
6118
6119 return result;
6120}
6121
6122static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6123{
6124 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6125 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6126
6127 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6128
6129 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6130 return result;
6131}
6132
6133static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6134{
6135 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6136 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6137
6138 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6139
6140 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6141 return result;
6142}
6143
6144static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6145{
6146 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6147 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6148
6149 QgsGeometry geom = lineGeom.interpolate( distance );
6150
6151 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6152 return result;
6153}
6154
6155static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6156{
6157 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6158 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6159 const bool use3DDistance = values.at( 2 ).toBool();
6160
6161 double x, y, z, distance;
6162
6164 if ( !line )
6165 {
6166 return QVariant();
6167 }
6168
6169 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6170 {
6171 QgsPoint point( x, y );
6172 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6173 {
6174 point.addZValue( z );
6175 }
6176 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6177 }
6178
6179 return QVariant();
6180}
6181
6182static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6183{
6184 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6185 if ( lineGeom.type() != Qgis::GeometryType::Line )
6186 {
6187 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6188 return QVariant();
6189 }
6190
6191 const QgsCurve *curve = nullptr;
6192 if ( !lineGeom.isMultipart() )
6193 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6194 else
6195 {
6197 {
6198 if ( collection->numGeometries() > 0 )
6199 {
6200 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6201 }
6202 }
6203 }
6204 if ( !curve )
6205 return QVariant();
6206
6207 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6208 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6209
6210 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6211 QgsGeometry result( std::move( substring ) );
6212 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6213}
6214
6215static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6216{
6217 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6218 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6219
6220 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6221}
6222
6223static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6224{
6225 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6226 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6227 if ( vertex < 0 )
6228 {
6229 //negative idx
6230 int count = geom.constGet()->nCoordinates();
6231 vertex = count + vertex;
6232 }
6233
6234 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6235}
6236
6237static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6238{
6239 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6240 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6241 if ( vertex < 0 )
6242 {
6243 //negative idx
6244 int count = geom.constGet()->nCoordinates();
6245 vertex = count + vertex;
6246 }
6247
6248 return geom.distanceToVertex( vertex );
6249}
6250
6251static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6252{
6253 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6254 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6255
6256 double distance = lineGeom.lineLocatePoint( pointGeom );
6257
6258 return distance >= 0 ? distance : QVariant();
6259}
6260
6261static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6262{
6263 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6264 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6265 const bool use3DDistance = values.at( 2 ).toBool();
6266
6267 double x, y, z, distance;
6268
6270 if ( !line )
6271 {
6272 return QVariant();
6273 }
6274
6275 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6276 return found ? distance : QVariant();
6277}
6278
6279static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6280{
6281 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6282 {
6283 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6284 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6285 }
6286
6287 if ( values.length() >= 1 )
6288 {
6289 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6290 return QVariant( qlonglong( std::round( number ) ) );
6291 }
6292
6293 return QVariant();
6294}
6295
6296static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6297{
6298 Q_UNUSED( values )
6299 Q_UNUSED( parent )
6300 return M_PI;
6301}
6302
6303static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6304{
6305 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6306 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6307 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6308 if ( places < 0 )
6309 {
6310 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6311 return QVariant();
6312 }
6313
6314 const bool omitGroupSeparator = values.value( 3 ).toBool();
6315 const bool trimTrailingZeros = values.value( 4 ).toBool();
6316
6317 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6318 if ( !omitGroupSeparator )
6319 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6320 else
6321 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6322
6323 QString res = locale.toString( value, 'f', places );
6324
6325 if ( trimTrailingZeros )
6326 {
6327 const QChar decimal = locale.decimalPoint().at( 0 );
6328 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6329
6330 if ( res.contains( decimal ) )
6331 {
6332 int trimPoint = res.length() - 1;
6333
6334 while ( res.at( trimPoint ) == zeroDigit )
6335 trimPoint--;
6336
6337 if ( res.at( trimPoint ) == decimal )
6338 trimPoint--;
6339
6340 res.truncate( trimPoint + 1 );
6341 }
6342 }
6343
6344 return res;
6345}
6346
6347static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6348{
6349 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6350 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6351 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6352
6353 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6354 if ( format.indexOf( "Z" ) > 0 )
6355 datetime = datetime.toUTC();
6356
6357 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6358 return locale.toString( datetime, format );
6359}
6360
6361static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6362{
6363 const QVariant variant = values.at( 0 );
6364 bool isQColor;
6365 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6366 if ( !color.isValid() )
6367 return QVariant();
6368
6369 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6370 if ( color.spec() == QColor::Spec::Cmyk )
6371 {
6372 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6373 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6374 }
6375 else
6376 {
6377 const float avg = ( color.redF() + color.greenF() + color.blueF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6378 color.setRgbF( avg, avg, avg, alpha );
6379 }
6380
6381 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6382}
6383
6384static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6385{
6386 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6387 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6388 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6389 if ( ratio > 1 )
6390 {
6391 ratio = 1;
6392 }
6393 else if ( ratio < 0 )
6394 {
6395 ratio = 0;
6396 }
6397
6398 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6399 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6400 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6401 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6402
6403 QColor newColor( red, green, blue, alpha );
6404
6405 return QgsSymbolLayerUtils::encodeColor( newColor );
6406}
6407
6408static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6409{
6410 const QVariant variant1 = values.at( 0 );
6411 const QVariant variant2 = values.at( 1 );
6412
6413 if ( variant1.userType() != variant2.userType() )
6414 {
6415 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6416 return QVariant();
6417 }
6418
6419 bool isQColor;
6420 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6421 if ( !color1.isValid() )
6422 return QVariant();
6423
6424 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6425 if ( !color2.isValid() )
6426 return QVariant();
6427
6428 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6429 {
6430 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6431 return QVariant();
6432 }
6433
6434 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6435
6436 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6437 // NOLINTBEGIN(bugprone-narrowing-conversions)
6438
6439 QColor newColor;
6440 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6441 if ( color1.spec() == QColor::Spec::Cmyk )
6442 {
6443 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6444 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6445 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6446 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6447 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6448 }
6449 else
6450 {
6451 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6452 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6453 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6454 newColor = QColor::fromRgbF( red, green, blue, alpha );
6455 }
6456
6457 // NOLINTEND(bugprone-narrowing-conversions)
6458
6459 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6460}
6461
6462static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6463{
6464 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6465 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6466 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6467 QColor color = QColor( red, green, blue );
6468 if ( ! color.isValid() )
6469 {
6470 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6471 color = QColor( 0, 0, 0 );
6472 }
6473
6474 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6475}
6476
6477static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6478{
6479 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6480 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6481 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6482 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6483 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6484 if ( ! color.isValid() )
6485 {
6486 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6487 return QVariant();
6488 }
6489
6490 return color;
6491}
6492
6493static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6494{
6495 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6496 QVariant value = node->eval( parent, context );
6497 if ( parent->hasEvalError() )
6498 {
6499 parent->setEvalErrorString( QString() );
6500 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6502 value = node->eval( parent, context );
6504 }
6505 return value;
6506}
6507
6508static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6509{
6510 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6512 QVariant value = node->eval( parent, context );
6514 if ( value.toBool() )
6515 {
6516 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6518 value = node->eval( parent, context );
6520 }
6521 else
6522 {
6523 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6525 value = node->eval( parent, context );
6527 }
6528 return value;
6529}
6530
6531static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6532{
6533 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6534 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6535 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6536 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6537 QColor color = QColor( red, green, blue, alpha );
6538 if ( ! color.isValid() )
6539 {
6540 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6541 color = QColor( 0, 0, 0 );
6542 }
6543 return QgsSymbolLayerUtils::encodeColor( color );
6544}
6545
6546QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6547{
6548 QgsGradientColorRamp expRamp;
6549 const QgsColorRamp *ramp = nullptr;
6550 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6551 {
6552 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6553 ramp = &expRamp;
6554 }
6555 else
6556 {
6557 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6558 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6559 if ( ! ramp )
6561 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6562 return QVariant();
6563 }
6564 }
6565
6566 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6567 QColor color = ramp->color( value );
6568 return color;
6569}
6570
6571QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6572{
6573 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6574 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6575}
6576
6577static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6578{
6579 // Hue ranges from 0 - 360
6580 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6581 // Saturation ranges from 0 - 100
6582 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6583 // Lightness ranges from 0 - 100
6584 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6585
6586 QColor color = QColor::fromHslF( hue, saturation, lightness );
6587
6588 if ( ! color.isValid() )
6589 {
6590 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6591 color = QColor( 0, 0, 0 );
6592 }
6593
6594 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6595}
6596
6597static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6598{
6599 // Hue ranges from 0 - 360
6600 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6601 // Saturation ranges from 0 - 100
6602 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6603 // Lightness ranges from 0 - 100
6604 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6605 // Alpha ranges from 0 - 255
6606 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6607
6608 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6609 if ( ! color.isValid() )
6610 {
6611 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6612 color = QColor( 0, 0, 0 );
6613 }
6614 return QgsSymbolLayerUtils::encodeColor( color );
6615}
6616
6617static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6618{
6619 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6620 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6621 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6622 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6623
6624 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6625 if ( ! color.isValid() )
6626 {
6627 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6628 return QVariant();
6629 }
6630
6631 return color;
6632}
6633
6634static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6635{
6636 // Hue ranges from 0 - 360
6637 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6638 // Saturation ranges from 0 - 100
6639 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6640 // Value ranges from 0 - 100
6641 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6642
6643 QColor color = QColor::fromHsvF( hue, saturation, value );
6644
6645 if ( ! color.isValid() )
6646 {
6647 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6648 color = QColor( 0, 0, 0 );
6649 }
6650
6651 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6652}
6653
6654static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6655{
6656 // Hue ranges from 0 - 360
6657 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6658 // Saturation ranges from 0 - 100
6659 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6660 // Value ranges from 0 - 100
6661 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6662 // Alpha ranges from 0 - 255
6663 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6664
6665 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6666 if ( ! color.isValid() )
6667 {
6668 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6669 color = QColor( 0, 0, 0 );
6670 }
6671 return QgsSymbolLayerUtils::encodeColor( color );
6672}
6673
6674static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6675{
6676 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6677 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6678 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6679 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6680 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6681
6682 if ( ! color.isValid() )
6683 {
6684 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6685 return QVariant();
6686 }
6687
6688 return color;
6689}
6690
6691static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6692{
6693 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6694 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6695 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6696 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6697 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6698
6699 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6700 if ( ! color.isValid() )
6701 {
6702 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6703 return QVariant();
6704 }
6705
6706 return color;
6707}
6708
6709static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6710{
6711 // Cyan ranges from 0 - 100
6712 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6713 // Magenta ranges from 0 - 100
6714 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6715 // Yellow ranges from 0 - 100
6716 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6717 // Black ranges from 0 - 100
6718 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6719
6720 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6721
6722 if ( ! color.isValid() )
6723 {
6724 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6725 color = QColor( 0, 0, 0 );
6726 }
6727
6728 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6729}
6730
6731static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6732{
6733 // Cyan ranges from 0 - 100
6734 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6735 // Magenta ranges from 0 - 100
6736 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6737 // Yellow ranges from 0 - 100
6738 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6739 // Black ranges from 0 - 100
6740 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6741 // Alpha ranges from 0 - 255
6742 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6743
6744 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6745 if ( ! color.isValid() )
6746 {
6747 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6748 color = QColor( 0, 0, 0 );
6749 }
6750 return QgsSymbolLayerUtils::encodeColor( color );
6751}
6752
6753static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6754{
6755 const QVariant variant = values.at( 0 );
6756 bool isQColor;
6757 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6758 if ( !color.isValid() )
6759 return QVariant();
6760
6761 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6762 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6763 return color.red();
6764 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6765 return color.green();
6766 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6767 return color.blue();
6768 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6769 return color.alpha();
6770 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6771 return static_cast< double >( color.hsvHueF() * 360 );
6772 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6773 return static_cast< double >( color.hsvSaturationF() * 100 );
6774 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6775 return static_cast< double >( color.valueF() * 100 );
6776 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6777 return static_cast< double >( color.hslHueF() * 360 );
6778 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6779 return static_cast< double >( color.hslSaturationF() * 100 );
6780 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6781 return static_cast< double >( color.lightnessF() * 100 );
6782 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6783 return static_cast< double >( color.cyanF() * 100 );
6784 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6785 return static_cast< double >( color.magentaF() * 100 );
6786 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6787 return static_cast< double >( color.yellowF() * 100 );
6788 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6789 return static_cast< double >( color.blackF() * 100 );
6790
6791 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6792 return QVariant();
6793}
6794
6795static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6796{
6797 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6798 if ( map.empty() )
6799 {
6800 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6801 return QVariant();
6802 }
6803
6804 QList< QColor > colors;
6806 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6807 {
6808 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6809 if ( !colors.last().isValid() )
6810 {
6811 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6812 return QVariant();
6813 }
6814
6815 double step = it.key().toDouble();
6816 if ( it == map.constBegin() )
6817 {
6818 if ( step != 0.0 )
6819 stops << QgsGradientStop( step, colors.last() );
6820 }
6821 else if ( it == map.constEnd() )
6822 {
6823 if ( step != 1.0 )
6824 stops << QgsGradientStop( step, colors.last() );
6825 }
6826 else
6827 {
6828 stops << QgsGradientStop( step, colors.last() );
6829 }
6830 }
6831 bool discrete = values.at( 1 ).toBool();
6832
6833 if ( colors.empty() )
6834 return QVariant();
6835
6836 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6837}
6838
6839static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6840{
6841 const QVariant variant = values.at( 0 );
6842 bool isQColor;
6843 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6844 if ( !color.isValid() )
6845 return QVariant();
6846
6847 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6848 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6849 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6850 color.setRed( std::clamp( value, 0, 255 ) );
6851 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6852 color.setGreen( std::clamp( value, 0, 255 ) );
6853 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6854 color.setBlue( std::clamp( value, 0, 255 ) );
6855 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6856 color.setAlpha( std::clamp( value, 0, 255 ) );
6857 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6858 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6859 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6860 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6861 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6862 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6863 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6864 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6865 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6866 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6867 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6868 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6869 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6870 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6871 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6872 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6873 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6874 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6875 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6876 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6877 else
6878 {
6879 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6880 return QVariant();
6881 }
6882 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6883}
6884
6885static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6886{
6887 const QVariant variant = values.at( 0 );
6888 bool isQColor;
6889 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6890 if ( !color.isValid() )
6891 return QVariant();
6892
6893 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6894
6895 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6896}
6897
6898static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6899{
6900 const QVariant variant = values.at( 0 );
6901 bool isQColor;
6902 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6903 if ( !color.isValid() )
6904 return QVariant();
6905
6906 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6907
6908 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6909}
6910
6911static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6912{
6913 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6914 QgsGeometry geom = feat.geometry();
6915 if ( !geom.isNull() )
6916 return QVariant::fromValue( geom );
6917 return QVariant();
6918}
6919
6920static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6921{
6922 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6923 if ( !feat.isValid() )
6924 return QVariant();
6925 return feat.id();
6926}
6927
6928static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6929{
6930 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6931 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
6932 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6933
6934 if ( !sCrs.isValid() )
6935 return QVariant::fromValue( fGeom );
6936
6937 if ( !dCrs.isValid() )
6938 return QVariant::fromValue( fGeom );
6939
6941 if ( context )
6942 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6943 QgsCoordinateTransform t( sCrs, dCrs, tContext );
6944 try
6945 {
6947 return QVariant::fromValue( fGeom );
6948 }
6949 catch ( QgsCsException &cse )
6950 {
6951 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6952 return QVariant();
6953 }
6954 return QVariant();
6955}
6956
6957
6958static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6959{
6960 bool foundLayer = false;
6961 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6962
6963 //no layer found
6964 if ( !featureSource || !foundLayer )
6965 {
6966 return QVariant();
6967 }
6968
6969 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6970
6972 req.setFilterFid( fid );
6973 req.setTimeout( 10000 );
6974 req.setRequestMayBeNested( true );
6975 if ( context )
6976 req.setFeedback( context->feedback() );
6977 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6978
6979 QgsFeature fet;
6980 QVariant result;
6981 if ( fIt.nextFeature( fet ) )
6982 result = QVariant::fromValue( fet );
6983
6984 return result;
6985}
6986
6987static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6988{
6989 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6990 bool foundLayer = false;
6991 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6992
6993 //no layer found
6994 if ( !featureSource || !foundLayer )
6995 {
6996 return QVariant();
6997 }
6999 QString cacheValueKey;
7000 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7001 {
7002 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7003
7004 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
7005 QString filterString;
7006 for ( ; i != attributeMap.constEnd(); ++i )
7007 {
7008 if ( !filterString.isEmpty() )
7009 {
7010 filterString.append( " AND " );
7011 }
7012 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7013 }
7014 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7015 if ( context && context->hasCachedValue( cacheValueKey ) )
7016 {
7017 return context->cachedValue( cacheValueKey );
7018 }
7019 req.setFilterExpression( filterString );
7020 }
7021 else
7022 {
7023 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7024 int attributeId = featureSource->fields().lookupField( attribute );
7025 if ( attributeId == -1 )
7026 {
7027 return QVariant();
7028 }
7029
7030 const QVariant &attVal = values.at( 2 );
7031
7032 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7033 if ( context && context->hasCachedValue( cacheValueKey ) )
7034 {
7035 return context->cachedValue( cacheValueKey );
7036 }
7037
7039 }
7040 req.setLimit( 1 );
7041 req.setTimeout( 10000 );
7042 req.setRequestMayBeNested( true );
7043 if ( context )
7044 req.setFeedback( context->feedback() );
7045 if ( !parent->needsGeometry() )
7046 {
7048 }
7049 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7050
7051 QgsFeature fet;
7052 QVariant res;
7053 if ( fIt.nextFeature( fet ) )
7054 {
7055 res = QVariant::fromValue( fet );
7056 }
7057
7058 if ( context )
7059 context->setCachedValue( cacheValueKey, res );
7060 return res;
7061}
7062
7063static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7064{
7065 QVariant result;
7066 QString fieldName;
7067
7068 if ( context )
7069 {
7070 if ( !values.isEmpty() )
7071 {
7072 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7073 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7074 fieldName = col->name();
7075 else if ( values.size() == 2 )
7076 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7077 }
7078
7079 QVariant value = values.at( 0 );
7080
7081 const QgsFields fields = context->fields();
7082 int fieldIndex = fields.lookupField( fieldName );
7083
7084 if ( fieldIndex == -1 )
7085 {
7086 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7087 }
7088 else
7089 {
7090 // TODO this function is NOT thread safe
7092 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7094
7095 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7096 if ( context->hasCachedValue( cacheValueKey ) )
7097 {
7098 return context->cachedValue( cacheValueKey );
7099 }
7100
7101 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7103
7104 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7105
7106 QVariant cache;
7107 if ( !context->hasCachedValue( cacheKey ) )
7108 {
7109 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7110 context->setCachedValue( cacheKey, cache );
7111 }
7112 else
7113 cache = context->cachedValue( cacheKey );
7114
7115 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7116
7117 context->setCachedValue( cacheValueKey, result );
7118 }
7119 }
7120 else
7121 {
7122 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7123 }
7124
7125 return result;
7126}
7127
7128static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7129{
7130 const QVariant data = values.at( 0 );
7131 const QMimeDatabase db;
7132 return db.mimeTypeForData( data.toByteArray() ).name();
7133}
7134
7135static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7136{
7137 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7138
7139 bool foundLayer = false;
7140 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
7141 {
7142 if ( !layer )
7143 return QVariant();
7144
7145 // here, we always prefer the layer metadata values over the older server-specific published values
7146 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7147 return layer->name();
7148 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7149 return layer->id();
7150 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7151 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7152 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7153 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7154 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7155 {
7156 QStringList keywords;
7157 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7158 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7159 {
7160 keywords.append( it.value() );
7161 }
7162 if ( !keywords.isEmpty() )
7163 return keywords;
7164 return layer->serverProperties()->keywordList();
7165 }
7166 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7167 return layer->serverProperties()->dataUrl();
7168 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7169 {
7170 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7171 }
7172 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7173 return layer->serverProperties()->attributionUrl();
7174 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7175 return layer->publicSource();
7176 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7177 return layer->minimumScale();
7178 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7179 return layer->maximumScale();
7180 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7181 return layer->isEditable();
7182 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7183 return layer->crs().authid();
7184 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7185 return layer->crs().toProj();
7186 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7187 return layer->crs().description();
7188 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7189 return layer->crs().ellipsoidAcronym();
7190 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7191 {
7192 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7193 QVariant result = QVariant::fromValue( extentGeom );
7194 return result;
7195 }
7196 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7197 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7198 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7199 {
7200 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7201 return decodedUri.value( u"path"_s );
7202 }
7203 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7204 {
7205 switch ( layer->type() )
7206 {
7208 return QCoreApplication::translate( "expressions", "Vector" );
7210 return QCoreApplication::translate( "expressions", "Raster" );
7212 return QCoreApplication::translate( "expressions", "Mesh" );
7214 return QCoreApplication::translate( "expressions", "Vector Tile" );
7216 return QCoreApplication::translate( "expressions", "Plugin" );
7218 return QCoreApplication::translate( "expressions", "Annotation" );
7220 return QCoreApplication::translate( "expressions", "Point Cloud" );
7222 return QCoreApplication::translate( "expressions", "Group" );
7224 return QCoreApplication::translate( "expressions", "Tiled Scene" );
7225 }
7226 }
7227 else
7228 {
7229 //vector layer methods
7230 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7231 if ( vLayer )
7232 {
7233 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7234 return vLayer->storageType();
7235 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7237 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7238 return QVariant::fromValue( vLayer->featureCount() );
7239 }
7240 }
7241
7242 return QVariant();
7243 }, foundLayer );
7244
7245 if ( !foundLayer )
7246 return QVariant();
7247 else
7248 return res;
7249}
7250
7251static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7252{
7253 const QString uriPart = values.at( 1 ).toString();
7254
7255 bool foundLayer = false;
7256
7257 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
7258 {
7259 if ( !layer->dataProvider() )
7260 {
7261 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7262 return QVariant();
7263 }
7264
7265 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7266
7267 if ( !uriPart.isNull() )
7268 {
7269 return decodedUri.value( uriPart );
7270 }
7271 else
7272 {
7273 return decodedUri;
7274 }
7275 }, foundLayer );
7276
7277 if ( !foundLayer )
7278 {
7279 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7280 return QVariant();
7281 }
7282 else
7283 {
7284 return res;
7285 }
7286}
7287
7288static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7289{
7290 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7291 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7292
7293 bool foundLayer = false;
7294 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
7295 {
7296 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7297 if ( !rl )
7298 return QVariant();
7299
7300 if ( band < 1 || band > rl->bandCount() )
7301 {
7302 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7303 return QVariant();
7304 }
7305
7307
7308 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7310 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7312 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7314 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7316 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7318 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7320 else
7321 {
7322 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7323 return QVariant();
7324 }
7325
7326 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7327 switch ( stat )
7328 {
7330 return stats.mean;
7332 return stats.stdDev;
7334 return stats.minimumValue;
7336 return stats.maximumValue;
7338 return stats.range;
7340 return stats.sum;
7341 default:
7342 break;
7343 }
7344 return QVariant();
7345 }, foundLayer );
7346
7347 if ( !foundLayer )
7348 {
7349#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7350 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7351#endif
7352 return QVariant();
7353 }
7354 else
7355 {
7356 return res;
7357 }
7358}
7359
7360static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7361{
7362 return values;
7363}
7364
7365static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7366{
7367 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7368 bool ascending = values.value( 1 ).toBool();
7369 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7370 return list;
7371}
7372
7373static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7374{
7375 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7376}
7377
7378static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7379{
7380 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7381}
7382
7383static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7384{
7385 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7386}
7387
7388static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7389{
7390 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7391 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7392 int match = 0;
7393 for ( const auto &item : listB )
7394 {
7395 if ( listA.contains( item ) )
7396 match++;
7397 }
7398
7399 return QVariant( match == listB.count() );
7400}
7401
7402static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7403{
7404 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7405}
7406
7407static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7408{
7409 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7410 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7411 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
7412 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7413 return list.at( list.length() + pos );
7414 return QVariant();
7415}
7416
7417static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7418{
7419 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7420 return list.value( 0 );
7421}
7422
7423static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7424{
7425 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7426 return list.value( list.size() - 1 );
7427}
7428
7429static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7430{
7431 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7432 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7433}
7434
7435static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7436{
7437 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7438 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7439}
7440
7441static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7442{
7443 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7444 int i = 0;
7445 double total = 0.0;
7446 for ( const QVariant &item : list )
7447 {
7448 switch ( item.userType() )
7449 {
7450 case QMetaType::Int:
7451 case QMetaType::UInt:
7452 case QMetaType::LongLong:
7453 case QMetaType::ULongLong:
7454 case QMetaType::Float:
7455 case QMetaType::Double:
7456 total += item.toDouble();
7457 ++i;
7458 break;
7459 }
7460 }
7461 return i == 0 ? QVariant() : total / i;
7462}
7463
7464static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7465{
7466 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7467 QVariantList numbers;
7468 for ( const auto &item : list )
7469 {
7470 switch ( item.userType() )
7471 {
7472 case QMetaType::Int:
7473 case QMetaType::UInt:
7474 case QMetaType::LongLong:
7475 case QMetaType::ULongLong:
7476 case QMetaType::Float:
7477 case QMetaType::Double:
7478 numbers.append( item );
7479 break;
7480 }
7481 }
7482 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7483 const int count = numbers.count();
7484 if ( count == 0 )
7485 {
7486 return QVariant();
7487 }
7488 else if ( count % 2 )
7489 {
7490 return numbers.at( count / 2 );
7491 }
7492 else
7493 {
7494 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7495 }
7496}
7497
7498static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7499{
7500 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7501 int i = 0;
7502 double total = 0.0;
7503 for ( const QVariant &item : list )
7504 {
7505 switch ( item.userType() )
7506 {
7507 case QMetaType::Int:
7508 case QMetaType::UInt:
7509 case QMetaType::LongLong:
7510 case QMetaType::ULongLong:
7511 case QMetaType::Float:
7512 case QMetaType::Double:
7513 total += item.toDouble();
7514 ++i;
7515 break;
7516 }
7517 }
7518 return i == 0 ? QVariant() : total;
7519}
7520
7521static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7522{
7523 QVariant result = value;
7524 ( void )result.convert( static_cast<int>( type ) );
7525 return result;
7526}
7527
7528static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7529{
7530 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7531 QHash< QVariant, int > hash;
7532 for ( const auto &item : list )
7533 {
7534 ++hash[item];
7535 }
7536 const QList< int > occurrences = hash.values();
7537 if ( occurrences.empty() )
7538 return QVariantList();
7539
7540 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7541
7542 const QString option = values.at( 1 ).toString();
7543 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7544 {
7545 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7546 }
7547 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7548 {
7549 if ( hash.isEmpty() )
7550 return QVariant();
7551
7552 return QVariant( hash.key( maxValue ) );
7553 }
7554 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7555 {
7556 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7557 }
7558 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7559 {
7560 if ( maxValue * 2 <= list.size() )
7561 return QVariant();
7562
7563 return QVariant( hash.key( maxValue ) );
7564 }
7565 else
7566 {
7567 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7568 return QVariant();
7569 }
7570}
7571
7572static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7573{
7574 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7575 QHash< QVariant, int > hash;
7576 for ( const auto &item : list )
7577 {
7578 ++hash[item];
7579 }
7580 const QList< int > occurrences = hash.values();
7581 if ( occurrences.empty() )
7582 return QVariantList();
7583
7584 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7585
7586 const QString option = values.at( 1 ).toString();
7587 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7588 {
7589 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7590 }
7591 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7592 {
7593 if ( hash.isEmpty() )
7594 return QVariant();
7595
7596 return QVariant( hash.key( minValue ) );
7597 }
7598 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7599 {
7600 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7601 }
7602 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7603 {
7604 if ( hash.isEmpty() )
7605 return QVariant();
7606
7607 // Remove the majority, all others are minority
7608 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7609 if ( maxValue * 2 > list.size() )
7610 hash.remove( hash.key( maxValue ) );
7611
7612 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7613 }
7614 else
7615 {
7616 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7617 return QVariant();
7618 }
7619}
7620
7621static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7622{
7623 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7624 list.append( values.at( 1 ) );
7625 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7626}
7627
7628static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7629{
7630 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7631 list.prepend( values.at( 1 ) );
7632 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7633}
7634
7635static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7636{
7637 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7638 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7639 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7640}
7641
7642static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7643{
7644 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7645 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7646 if ( position < 0 )
7647 position = position + list.length();
7648 if ( position >= 0 && position < list.length() )
7649 list.removeAt( position );
7650 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7651}
7652
7653static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7654{
7655 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7656 return QVariant();
7657
7658 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7659
7660 const QVariant toRemove = values.at( 1 );
7661 if ( QgsVariantUtils::isNull( toRemove ) )
7662 {
7663 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7664 {
7665 return QgsVariantUtils::isNull( element );
7666 } ), list.end() );
7667 }
7668 else
7669 {
7670 list.removeAll( toRemove );
7671 }
7672 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7673}
7674
7675static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7676{
7677 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7678 {
7679 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7680
7681 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7682 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7683 {
7684 int index = list.indexOf( it.key() );
7685 while ( index >= 0 )
7686 {
7687 list.replace( index, it.value() );
7688 index = list.indexOf( it.key() );
7689 }
7690 }
7691
7692 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7693 }
7694 else if ( values.count() == 3 )
7695 {
7696 QVariantList before;
7697 QVariantList after;
7698 bool isSingleReplacement = false;
7699
7700 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7701 {
7702 before = QVariantList() << values.at( 1 );
7703 }
7704 else
7705 {
7706 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7707 }
7708
7709 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7710 {
7711 after = QVariantList() << values.at( 2 );
7712 isSingleReplacement = true;
7713 }
7714 else
7715 {
7716 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7717 }
7718
7719 if ( !isSingleReplacement && before.length() != after.length() )
7720 {
7721 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7722 return QVariant();
7723 }
7724
7725 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7726 for ( int i = 0; i < before.length(); i++ )
7727 {
7728 int index = list.indexOf( before.at( i ) );
7729 while ( index >= 0 )
7730 {
7731 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7732 index = list.indexOf( before.at( i ) );
7733 }
7734 }
7735
7736 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7737 }
7738 else
7739 {
7740 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7741 return QVariant();
7742 }
7743}
7744
7745static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7746{
7747 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7748 QVariantList list_new;
7749
7750 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7751 {
7752 while ( list.removeOne( cur ) )
7753 {
7754 list_new.append( cur );
7755 }
7756 }
7757
7758 list_new.append( list );
7759
7760 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7761}
7762
7763static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7764{
7765 QVariantList list;
7766 for ( const QVariant &cur : values )
7767 {
7768 list += QgsExpressionUtils::getListValue( cur, parent );
7769 }
7770 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7771}
7772
7773static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7774{
7775 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7776 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7777 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7778 int slice_length = 0;
7779 // negative positions means positions taken relative to the end of the array
7780 if ( start_pos < 0 )
7781 {
7782 start_pos = list.length() + start_pos;
7783 }
7784 if ( end_pos >= 0 )
7785 {
7786 slice_length = end_pos - start_pos + 1;
7787 }
7788 else
7789 {
7790 slice_length = list.length() + end_pos - start_pos + 1;
7791 }
7792 //avoid negative lengths in QList.mid function
7793 if ( slice_length < 0 )
7794 {
7795 slice_length = 0;
7796 }
7797 list = list.mid( start_pos, slice_length );
7798 return list;
7799}
7800
7801static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7802{
7803 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7804 std::reverse( list.begin(), list.end() );
7805 return list;
7806}
7807
7808static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7809{
7810 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7811 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7812 for ( const QVariant &cur : array2 )
7813 {
7814 if ( array1.contains( cur ) )
7815 return QVariant( true );
7816 }
7817 return QVariant( false );
7818}
7819
7820static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7821{
7822 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7823
7824 QVariantList distinct;
7825
7826 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7827 {
7828 if ( !distinct.contains( *it ) )
7829 {
7830 distinct += ( *it );
7831 }
7832 }
7833
7834 return distinct;
7835}
7836
7837static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7838{
7839 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7840 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7841 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7842
7843 QString str;
7844
7845 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7846 {
7847 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7848 if ( it != ( array.constEnd() - 1 ) )
7849 {
7850 str += delimiter;
7851 }
7852 }
7853
7854 return QVariant( str );
7855}
7856
7857static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7858{
7859 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7860 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7861 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7862
7863 QStringList list = str.split( delimiter );
7864 QVariantList array;
7865
7866 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7867 {
7868 array += ( !( *it ).isEmpty() ) ? *it : empty;
7869 }
7870
7871 return array;
7872}
7873
7874static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7875{
7876 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7877 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7878 if ( document.isNull() )
7879 return QVariant();
7880
7881 return document.toVariant();
7882}
7883
7884static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7885{
7886 Q_UNUSED( parent )
7887 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7888 return QString( document.toJson( QJsonDocument::Compact ) );
7889}
7890
7891static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7892{
7893 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7894 if ( str.isEmpty() )
7895 return QVariantMap();
7896 str = str.trimmed();
7897
7898 return QgsHstoreUtils::parse( str );
7899}
7900
7901static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7902{
7903 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7904 return QgsHstoreUtils::build( map );
7905}
7906
7907static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7908{
7909 QVariantMap result;
7910 for ( int i = 0; i + 1 < values.length(); i += 2 )
7911 {
7912 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7913 }
7914 return result;
7915}
7916
7917static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7918{
7919 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7920 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7921 QVariantMap resultMap;
7922
7923 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7924 {
7925 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7926 }
7927
7928 return resultMap;
7929}
7930
7931static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7932{
7933 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7934}
7935
7936static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7937{
7938 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7939}
7940
7941static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7942{
7943 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7944 map.remove( values.at( 1 ).toString() );
7945 return map;
7946}
7947
7948static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7949{
7950 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7951 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7952 return map;
7953}
7954
7955static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7956{
7957 QVariantMap result;
7958 for ( const QVariant &cur : values )
7959 {
7960 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7961 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7962 result.insert( it.key(), it.value() );
7963 }
7964 return result;
7965}
7966
7967static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7968{
7969 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7970}
7971
7972static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7973{
7974 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7975}
7976
7977static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7978{
7979 const QString envVarName = values.at( 0 ).toString();
7980 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7981 return QVariant();
7982
7983 return QProcessEnvironment::systemEnvironment().value( envVarName );
7984}
7985
7986static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7987{
7988 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7989 if ( parent->hasEvalError() )
7990 {
7991 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "base_file_name"_L1 ) );
7992 return QVariant();
7993 }
7994 return QFileInfo( file ).completeBaseName();
7995}
7996
7997static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7998{
7999 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8000 if ( parent->hasEvalError() )
8001 {
8002 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_suffix"_L1 ) );
8003 return QVariant();
8004 }
8005 return QFileInfo( file ).completeSuffix();
8006}
8007
8008static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8009{
8010 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8011 if ( parent->hasEvalError() )
8012 {
8013 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8014 return QVariant();
8015 }
8016 return QFileInfo::exists( file );
8017}
8018
8019static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8020{
8021 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8022 if ( parent->hasEvalError() )
8023 {
8024 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_name"_L1 ) );
8025 return QVariant();
8026 }
8027 return QFileInfo( file ).fileName();
8028}
8029
8030static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8031{
8032 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8033 if ( parent->hasEvalError() )
8034 {
8035 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_file"_L1 ) );
8036 return QVariant();
8037 }
8038 return QFileInfo( file ).isFile();
8039}
8040
8041static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8042{
8043 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8044 if ( parent->hasEvalError() )
8045 {
8046 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8047 return QVariant();
8048 }
8049 return QFileInfo( file ).isDir();
8050}
8051
8052static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8053{
8054 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8055 if ( parent->hasEvalError() )
8056 {
8057 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8058 return QVariant();
8059 }
8060 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8061}
8062
8063static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8064{
8065 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8066 if ( parent->hasEvalError() )
8067 {
8068 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8069 return QVariant();
8070 }
8071 return QFileInfo( file ).size();
8072}
8073
8074static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8075{
8076 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8077}
8078
8079static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8080{
8081 QVariant hash;
8082 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8083 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8084
8085 if ( method == "md4"_L1 )
8086 {
8087 hash = fcnHash( str, QCryptographicHash::Md4 );
8088 }
8089 else if ( method == "md5"_L1 )
8090 {
8091 hash = fcnHash( str, QCryptographicHash::Md5 );
8092 }
8093 else if ( method == "sha1"_L1 )
8094 {
8095 hash = fcnHash( str, QCryptographicHash::Sha1 );
8096 }
8097 else if ( method == "sha224"_L1 )
8098 {
8099 hash = fcnHash( str, QCryptographicHash::Sha224 );
8100 }
8101 else if ( method == "sha256"_L1 )
8102 {
8103 hash = fcnHash( str, QCryptographicHash::Sha256 );
8104 }
8105 else if ( method == "sha384"_L1 )
8106 {
8107 hash = fcnHash( str, QCryptographicHash::Sha384 );
8108 }
8109 else if ( method == "sha512"_L1 )
8110 {
8111 hash = fcnHash( str, QCryptographicHash::Sha512 );
8112 }
8113 else if ( method == "sha3_224"_L1 )
8114 {
8115 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8116 }
8117 else if ( method == "sha3_256"_L1 )
8118 {
8119 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8120 }
8121 else if ( method == "sha3_384"_L1 )
8122 {
8123 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8124 }
8125 else if ( method == "sha3_512"_L1 )
8126 {
8127 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8128 }
8129 else if ( method == "keccak_224"_L1 )
8130 {
8131 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8132 }
8133 else if ( method == "keccak_256"_L1 )
8134 {
8135 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8136 }
8137 else if ( method == "keccak_384"_L1 )
8138 {
8139 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8140 }
8141 else if ( method == "keccak_512"_L1 )
8142 {
8143 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8144 }
8145 else
8146 {
8147 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8148 }
8149 return hash;
8150}
8151
8152static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8153{
8154 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8155}
8156
8157static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8158{
8159 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8160}
8161
8162static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8163{
8164 const QByteArray input = values.at( 0 ).toByteArray();
8165 return QVariant( QString( input.toBase64() ) );
8166}
8167
8168static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8169{
8170 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8171 QUrlQuery query;
8172 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8173 {
8174 query.addQueryItem( it.key(), it.value().toString() );
8175 }
8176 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8177}
8178
8179static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8180{
8181 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8182 const QByteArray base64 = value.toLocal8Bit();
8183 const QByteArray decoded = QByteArray::fromBase64( base64 );
8184 return QVariant( decoded );
8185}
8186
8187typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
8188
8189static 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 )
8190{
8191
8192 if ( ! context )
8193 {
8194 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8195 return QVariant();
8196 }
8197
8198 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8199 // TODO this function is NOT thread safe
8201 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8203
8204 QgsFeatureRequest request;
8205 request.setTimeout( 10000 );
8206 request.setRequestMayBeNested( true );
8207 request.setFeedback( context->feedback() );
8208
8209 // First parameter is the overlay layer
8210 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8212
8213 const bool layerCanBeCached = node->isStatic( parent, context );
8214 QVariant targetLayerValue = node->eval( parent, context );
8216
8217 // Second parameter is the expression to evaluate (or null for testonly)
8218 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8220 QString subExpString = node->dump();
8221
8222 bool testOnly = ( subExpString == "NULL" );
8223 // TODO this function is NOT thread safe
8225 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8227 if ( !targetLayer ) // No layer, no joy
8228 {
8229 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8230 return QVariant();
8231 }
8232
8233 // Third parameter is the filtering expression
8234 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8236 QString filterString = node->dump();
8237 if ( filterString != "NULL" )
8238 {
8239 request.setFilterExpression( filterString ); //filter cached features
8240 }
8241
8242 // Fourth parameter is the limit
8243 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8245 QVariant limitValue = node->eval( parent, context );
8247 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8248
8249 // Fifth parameter (for nearest only) is the max distance
8250 double max_distance = 0;
8251 if ( isNearestFunc ) //maxdistance param handling
8252 {
8253 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8255 QVariant distanceValue = node->eval( parent, context );
8257 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8258 }
8259
8260 // Fifth or sixth (for nearest only) parameter is the cache toggle
8261 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
8263 QVariant cacheValue = node->eval( parent, context );
8265 bool cacheEnabled = cacheValue.toBool();
8266
8267 // Sixth parameter (for intersects only) is the min overlap (area or length)
8268 // Seventh parameter (for intersects only) is the min inscribed circle radius
8269 // Eighth parameter (for intersects only) is the return_details
8270 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
8271 double minOverlap { -1 };
8272 double minInscribedCircleRadius { -1 };
8273 bool returnDetails = false; //#spellok
8274 bool sortByMeasure = false;
8275 bool sortAscending = false;
8276 bool requireMeasures = false;
8277 bool overlapOrRadiusFilter = false;
8278 if ( isIntersectsFunc )
8279 {
8280
8281 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8283 const QVariant minOverlapValue = node->eval( parent, context );
8285 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8286 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8288 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8290 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8291 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8292 // Return measures is only effective when an expression is set
8293 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8294 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8295 // Sort by measures is only effective when an expression is set
8296 const QString sorting { node->eval( parent, context ).toString().toLower() };
8297 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8298 sortAscending = sorting.startsWith( "asc" );
8299 requireMeasures = sortByMeasure || returnDetails; //#spellok
8300 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8301 }
8302
8303
8304 FEAT_FROM_CONTEXT( context, feat )
8305 const QgsGeometry geometry = feat.geometry();
8306
8307 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8308 {
8309 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8310 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8311 }
8312
8313 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8314
8315 QgsRectangle intDomain = geometry.boundingBox();
8316 if ( bboxGrow != 0 )
8317 {
8318 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8319 }
8320
8321 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8322
8323 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8324 // Otherwise, it can be toggled by the user
8325 QgsSpatialIndex spatialIndex;
8326 QgsVectorLayer *cachedTarget;
8327 QList<QgsFeature> features;
8328 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8329 {
8330 // If the cache (local spatial index) is enabled, we materialize the whole
8331 // layer, then do the request on that layer instead.
8332 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8333 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8334
8335 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8336 {
8337 cachedTarget = targetLayer->materialize( request );
8338 if ( layerCanBeCached )
8339 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8340 }
8341 else
8342 {
8343 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8344 }
8345
8346 if ( !context->hasCachedValue( cacheIndex ) )
8347 {
8348 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8349 if ( layerCanBeCached )
8350 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8351 }
8352 else
8353 {
8354 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8355 }
8356
8357 QList<QgsFeatureId> fidsList;
8358 if ( isNearestFunc )
8359 {
8360 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8361 }
8362 else
8363 {
8364 fidsList = spatialIndex.intersects( intDomain );
8365 }
8366
8367 QListIterator<QgsFeatureId> i( fidsList );
8368 while ( i.hasNext() )
8369 {
8370 QgsFeatureId fId2 = i.next();
8371 if ( sameLayers && feat.id() == fId2 )
8372 continue;
8373 features.append( cachedTarget->getFeature( fId2 ) );
8374 }
8375
8376 }
8377 else
8378 {
8379 // If the cache (local spatial index) is not enabled, we directly
8380 // get the features from the target layer
8381 request.setFilterRect( intDomain );
8382 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8383 QgsFeature feat2;
8384 while ( fit.nextFeature( feat2 ) )
8385 {
8386 if ( sameLayers && feat.id() == feat2.id() )
8387 continue;
8388 features.append( feat2 );
8389 }
8390 }
8391
8392 QgsExpression subExpression;
8393 QgsExpressionContext subContext;
8394 if ( !testOnly )
8395 {
8396 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8397 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8398
8399 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8400 {
8401 subExpression = QgsExpression( subExpString );
8403 subExpression.prepare( &subContext );
8404 }
8405 else
8406 {
8407 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8408 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8409 }
8410 }
8411
8412 // //////////////////////////////////////////////////////////////////
8413 // Helper functions for geometry tests
8414
8415 // Test function for linestring geometries, returns TRUE if test passes
8416 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double & overlapValue ) -> bool
8417 {
8418 bool testResult { false };
8419 // For return measures:
8420 QVector<double> overlapValues;
8421 const QgsGeometry merged { intersection.mergeLines() };
8422 for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
8423 {
8425 // Check min overlap for intersection (if set)
8426 if ( minOverlap != -1 || requireMeasures )
8427 {
8428 overlapValue = geom->length();
8429 overlapValues.append( overlapValue );
8430 if ( minOverlap != -1 )
8431 {
8432 if ( overlapValue >= minOverlap )
8433 {
8434 testResult = true;
8435 }
8436 else
8437 {
8438 continue;
8439 }
8440 }
8441 }
8442 }
8443
8444 if ( ! overlapValues.isEmpty() )
8445 {
8446 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8447 }
8448
8449 return testResult;
8450 };
8451
8452 // Test function for polygon geometries, returns TRUE if test passes
8453 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
8454 {
8455 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8456 bool testResult { false };
8457 // For return measures:
8458 QVector<double> overlapValues;
8459 QVector<double> radiusValues;
8460 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8461 {
8463 // Check min overlap for intersection (if set)
8464 if ( minOverlap != -1 || requireMeasures )
8465 {
8466 overlapValue = geom->area();
8467 overlapValues.append( geom->area() );
8468 if ( minOverlap != - 1 )
8469 {
8470 if ( overlapValue >= minOverlap )
8471 {
8472 testResult = true;
8473 }
8474 else
8475 {
8476 continue;
8477 }
8478 }
8479 }
8480
8481 // Check min inscribed circle radius for intersection (if set)
8482 if ( minInscribedCircleRadius != -1 || requireMeasures )
8483 {
8484 const QgsRectangle bbox = geom->boundingBox();
8485 const double width = bbox.width();
8486 const double height = bbox.height();
8487 const double size = width > height ? width : height;
8488 const double tolerance = size / 100.0;
8489 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8490 testResult = radiusValue >= minInscribedCircleRadius;
8491 radiusValues.append( radiusValues );
8492 }
8493 } // end for parts
8494
8495 // Get the max values
8496 if ( !radiusValues.isEmpty() )
8497 {
8498 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8499 }
8500
8501 if ( ! overlapValues.isEmpty() )
8502 {
8503 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8504 }
8505
8506 return testResult;
8507
8508 };
8509
8510
8511 bool found = false;
8512 int foundCount = 0;
8513 QVariantList results;
8514
8515 QListIterator<QgsFeature> i( features );
8516 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8517 {
8518
8519 QgsFeature feat2 = i.next();
8520
8521
8522 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8523 {
8524
8525 double overlapValue = -1;
8526 double radiusValue = -1;
8527
8528 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8529 {
8530
8531 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8532
8533 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8534 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8535 {
8536 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8537 intersection = QgsGeometry();
8538 QgsMultiPolygonXY poly;
8539 QgsMultiPolylineXY line;
8540 QgsMultiPointXY point;
8541 for ( const auto &geom : std::as_const( geometries ) )
8542 {
8543 switch ( geom.type() )
8544 {
8546 {
8547 poly.append( geom.asPolygon() );
8548 break;
8549 }
8551 {
8552 line.append( geom.asPolyline() );
8553 break;
8554 }
8556 {
8557 point.append( geom.asPoint() );
8558 break;
8559 }
8562 {
8563 break;
8564 }
8565 }
8566 }
8567
8568 switch ( geometry.type() )
8569 {
8571 {
8572 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8573 break;
8574 }
8576 {
8577 intersection = QgsGeometry::fromMultiPolylineXY( line );
8578 break;
8579 }
8581 {
8582 intersection = QgsGeometry::fromMultiPointXY( point );
8583 break;
8584 }
8587 {
8588 break;
8589 }
8590 }
8591 }
8592
8593 // Depending on the intersection geometry type and on the geometry type of
8594 // the tested geometry we can run different tests and collect different measures
8595 // that can be used for sorting (if required).
8596 switch ( intersection.type() )
8597 {
8598
8600 {
8601
8602 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8603 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8604
8605 if ( ! testResult && overlapOrRadiusFilter )
8606 {
8607 continue;
8608 }
8609
8610 break;
8611 }
8612
8614 {
8615
8616 // If the intersection is a linestring and a minimum circle is required
8617 // we can discard this result immediately.
8618 if ( minInscribedCircleRadius != -1 )
8619 {
8620 continue;
8621 }
8622
8623 // Otherwise a test for the overlap value is performed.
8624 const bool testResult { testLinestring( intersection, overlapValue ) };
8625
8626 if ( ! testResult && overlapOrRadiusFilter )
8627 {
8628 continue;
8629 }
8630
8631 break;
8632 }
8633
8635 {
8636
8637 // If the intersection is a point and a minimum circle is required
8638 // we can discard this result immediately.
8639 if ( minInscribedCircleRadius != -1 )
8640 {
8641 continue;
8642 }
8643
8644 bool testResult { false };
8645 if ( minOverlap != -1 || requireMeasures )
8646 {
8647 // Initially set this to 0 because it's a point intersection...
8648 overlapValue = 0;
8649 // ... but if the target geometry is not a point and the source
8650 // geometry is a point, we must record the length or the area
8651 // of the intersected geometry and use that as a measure for
8652 // sorting or reporting.
8653 if ( geometry.type() == Qgis::GeometryType::Point )
8654 {
8655 switch ( feat2.geometry().type() )
8656 {
8660 {
8661 break;
8662 }
8664 {
8665 testResult = testLinestring( feat2.geometry(), overlapValue );
8666 break;
8667 }
8669 {
8670 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8671 break;
8672 }
8673 }
8674 }
8675
8676 if ( ! testResult && overlapOrRadiusFilter )
8677 {
8678 continue;
8679 }
8680
8681 }
8682 break;
8683 }
8684
8687 {
8688 continue;
8689 }
8690 }
8691 }
8692
8693 found = true;
8694 foundCount++;
8695
8696 // We just want a single boolean result if there is any intersect: finish and return true
8697 if ( testOnly )
8698 break;
8699
8700 if ( !invert )
8701 {
8702 // We want a list of attributes / geometries / other expression values, evaluate now
8703 subContext.setFeature( feat2 );
8704 const QVariant expResult = subExpression.evaluate( &subContext );
8705
8706 if ( requireMeasures )
8707 {
8708 QVariantMap resultRecord;
8709 resultRecord.insert( u"id"_s, feat2.id() );
8710 resultRecord.insert( u"result"_s, expResult );
8711 // Overlap is always added because return measures was set
8712 resultRecord.insert( u"overlap"_s, overlapValue );
8713 // Radius is only added when is different than -1 (because for linestrings is not set)
8714 if ( radiusValue != -1 )
8715 {
8716 resultRecord.insert( u"radius"_s, radiusValue );
8717 }
8718 results.append( resultRecord );
8719 }
8720 else
8721 {
8722 results.append( expResult );
8723 }
8724 }
8725 else
8726 {
8727 // If not, results is a list of found ids, which we'll inverse and evaluate below
8728 results.append( feat2.id() );
8729 }
8730 }
8731 }
8732
8733 if ( testOnly )
8734 {
8735 if ( invert )
8736 found = !found;//for disjoint condition
8737 return found;
8738 }
8739
8740 if ( !invert )
8741 {
8742 if ( requireMeasures )
8743 {
8744 if ( sortByMeasure )
8745 {
8746 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8747 {
8748 return sortAscending ?
8749 recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
8750 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
8751 } );
8752 }
8753 // Resize
8754 if ( limit > 0 && results.size() > limit )
8755 {
8756 results.erase( results.begin() + limit );
8757 }
8758
8759 if ( ! returnDetails ) //#spellok
8760 {
8761 QVariantList expResults;
8762 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8763 {
8764 expResults.append( it->toMap().value( u"result"_s ) );
8765 }
8766 return expResults;
8767 }
8768 }
8769
8770 return results;
8771 }
8772
8773 // for disjoint condition returns the results for cached layers not intersected feats
8774 QVariantList disjoint_results;
8775 QgsFeature feat2;
8776 QgsFeatureRequest request2;
8777 request2.setLimit( limit );
8778 if ( context )
8779 request2.setFeedback( context->feedback() );
8780 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8781 while ( fi.nextFeature( feat2 ) )
8782 {
8783 if ( !results.contains( feat2.id() ) )
8784 {
8785 subContext.setFeature( feat2 );
8786 disjoint_results.append( subExpression.evaluate( &subContext ) );
8787 }
8788 }
8789 return disjoint_results;
8790
8791}
8792
8793// Intersect functions:
8794
8795static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8796{
8797 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8798}
8799
8800static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8801{
8802 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8803}
8804
8805static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8806{
8807 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8808}
8809
8810static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8811{
8812 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8813}
8814
8815static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8816{
8817 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8818}
8819
8820static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8821{
8822 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8823}
8825static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8826{
8827 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8828}
8829
8830static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8831{
8832 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8833}
8834
8835const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8836{
8837 // The construction of the list isn't thread-safe, and without the mutex,
8838 // crashes in the WFS provider may occur, since it can parse expressions
8839 // in parallel.
8840 // The mutex needs to be recursive.
8841 QMutexLocker locker( &sFunctionsMutex );
8842
8843 QList<QgsExpressionFunction *> &functions = *sFunctions();
8844
8845 if ( functions.isEmpty() )
8846 {
8848 << QgsExpressionFunction::Parameter( u"expression"_s )
8849 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
8850 << QgsExpressionFunction::Parameter( u"filter"_s, true );
8851
8852 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8853 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
8854 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
8855
8856 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8857 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
8858
8859 functions
8860 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
8861 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
8862 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
8863 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnAzimuth, u"GeometryGroup"_s )
8864 << new QgsStaticExpressionFunction( u"bearing"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ) << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ), fcnBearing, u"GeometryGroup"_s )
8865 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnInclination, u"GeometryGroup"_s )
8866 << new QgsStaticExpressionFunction( u"project"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ) << QgsExpressionFunction::Parameter( u"azimuth"_s ) << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ), fcnProject, u"GeometryGroup"_s )
8867 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
8868 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
8869 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
8870 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
8871 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
8872 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
8873 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
8874 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
8875 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
8876 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
8877 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
8878 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
8879 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
8880
8881 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( u"rand"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ), fcnRnd, u"Math"_s );
8882 randFunc->setIsStatic( false );
8883 functions << randFunc;
8884
8885 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( u"randf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 ) << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 ) << QgsExpressionFunction::Parameter( u"seed"_s, true ), fcnRndF, u"Math"_s );
8886 randfFunc->setIsStatic( false );
8887 functions << randfFunc;
8888
8889 functions
8890 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8891 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8892 << new QgsStaticExpressionFunction( u"clamp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"max"_s ), fcnClamp, u"Math"_s )
8893 << new QgsStaticExpressionFunction( u"scale_linear"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"domain_min"_s ) << QgsExpressionFunction::Parameter( u"domain_max"_s ) << QgsExpressionFunction::Parameter( u"range_min"_s ) << QgsExpressionFunction::Parameter( u"range_max"_s ), fcnLinearScale, u"Math"_s )
8894 << new QgsStaticExpressionFunction( u"scale_polynomial"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"domain_min"_s ) << QgsExpressionFunction::Parameter( u"domain_max"_s ) << QgsExpressionFunction::Parameter( u"range_min"_s ) << QgsExpressionFunction::Parameter( u"range_max"_s ) << QgsExpressionFunction::Parameter( u"exponent"_s ), fcnPolynomialScale, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"scale_exp"_s )
8895 << new QgsStaticExpressionFunction( u"scale_exponential"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"domain_min"_s ) << QgsExpressionFunction::Parameter( u"domain_max"_s ) << QgsExpressionFunction::Parameter( u"range_min"_s ) << QgsExpressionFunction::Parameter( u"range_max"_s ) << QgsExpressionFunction::Parameter( u"exponent"_s ), fcnExponentialScale, u"Math"_s )
8896 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
8897 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
8898 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
8899 << new QgsStaticExpressionFunction( u"to_bool"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBool, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"tobool"_s, /* handlesNull = */ true )
8900 << new QgsStaticExpressionFunction( u"to_int"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToInt, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"toint"_s )
8901 << new QgsStaticExpressionFunction( u"to_real"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToReal, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"toreal"_s )
8902 << new QgsStaticExpressionFunction( u"to_string"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToString, QStringList() << u"Conversions"_s << u"String"_s, QString(), false, QSet<QString>(), false, QStringList() << u"tostring"_s )
8903 << new QgsStaticExpressionFunction( u"to_datetime"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnToDateTime, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todatetime"_s )
8904 << new QgsStaticExpressionFunction( u"to_date"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnToDate, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todate"_s )
8905 << new QgsStaticExpressionFunction( u"to_time"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnToTime, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"totime"_s )
8906 << new QgsStaticExpressionFunction( u"to_interval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToInterval, QStringList() << u"Conversions"_s << u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"tointerval"_s )
8907 << new QgsStaticExpressionFunction( u"to_dm"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"axis"_s ) << QgsExpressionFunction::Parameter( u"precision"_s ) << QgsExpressionFunction::Parameter( u"formatting"_s, true ), fcnToDegreeMinute, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todm"_s )
8908 << new QgsStaticExpressionFunction( u"to_dms"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"axis"_s ) << QgsExpressionFunction::Parameter( u"precision"_s ) << QgsExpressionFunction::Parameter( u"formatting"_s, true ), fcnToDegreeMinuteSecond, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todms"_s )
8909 << new QgsStaticExpressionFunction( u"to_decimal"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToDecimal, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todecimal"_s )
8910 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter{ u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
8911 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter{ u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
8912 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter{ u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
8913 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
8914 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
8915 << new QgsStaticExpressionFunction( u"if"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"condition"_s ) << QgsExpressionFunction::Parameter( u"result_when_true"_s ) << QgsExpressionFunction::Parameter( u"result_when_false"_s ), fcnIf, u"Conditionals"_s, QString(), false, QSet<QString>(), true )
8916 << new QgsStaticExpressionFunction( u"try"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ), fcnTry, u"Conditionals"_s, QString(), false, QSet<QString>(), true )
8917
8918 << new QgsStaticExpressionFunction( u"aggregate"_s,
8920 << QgsExpressionFunction::Parameter( u"layer"_s )
8921 << QgsExpressionFunction::Parameter( u"aggregate"_s )
8922 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
8923 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
8924 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
8925 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
8926 fcnAggregate,
8927 u"Aggregates"_s,
8928 QString(),
8929 []( const QgsExpressionNodeFunction * node )
8930 {
8931 // usesGeometry callback: return true if @parent variable is referenced
8932
8933 if ( !node )
8934 return true;
8935
8936 if ( !node->args() )
8937 return false;
8938
8939 QSet<QString> referencedVars;
8940 if ( node->args()->count() > 2 )
8941 {
8942 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8943 referencedVars = subExpressionNode->referencedVariables();
8944 }
8945
8946 if ( node->args()->count() > 3 )
8947 {
8948 QgsExpressionNode *filterNode = node->args()->at( 3 );
8949 referencedVars.unite( filterNode->referencedVariables() );
8950 }
8951 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
8952 },
8953 []( const QgsExpressionNodeFunction * node )
8954 {
8955 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8956
8957 if ( !node )
8958 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8959
8960 if ( !node->args() )
8961 return QSet<QString>();
8962
8963 QSet<QString> referencedCols;
8964 QSet<QString> referencedVars;
8965
8966 if ( node->args()->count() > 2 )
8967 {
8968 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8969 referencedVars = subExpressionNode->referencedVariables();
8970 referencedCols = subExpressionNode->referencedColumns();
8971 }
8972 if ( node->args()->count() > 3 )
8973 {
8974 QgsExpressionNode *filterNode = node->args()->at( 3 );
8975 referencedVars = filterNode->referencedVariables();
8976 referencedCols.unite( filterNode->referencedColumns() );
8977 }
8978
8979 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
8980 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8981 else
8982 return referencedCols;
8983 },
8984 true
8985 )
8986
8987 << new QgsStaticExpressionFunction( u"relation_aggregate"_s, QgsExpressionFunction::ParameterList()
8988 << QgsExpressionFunction::Parameter( u"relation"_s )
8989 << QgsExpressionFunction::Parameter( u"aggregate"_s )
8990 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
8991 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
8992 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
8993 fcnAggregateRelation, u"Aggregates"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8994
8995 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
8996 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
8997 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
8998 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
8999 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9000 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9001 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9002 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9003 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9004 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9005 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9006 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9007 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9008 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9009 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9010 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9011 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9012 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9013 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9014 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9015 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9016
9017 << new QgsStaticExpressionFunction( u"regexp_match"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpMatch, QStringList() << u"Conditionals"_s << u"String"_s )
9018 << new QgsStaticExpressionFunction( u"regexp_matches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ), fcnRegexpMatches, u"Arrays"_s )
9019
9020 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9021 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s )
9022 << QgsExpressionFunction::Parameter( u"datetime2"_s ),
9023 fcnAge, u"Date and Time"_s )
9024 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9025 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9026 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9027 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9028 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9029 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9030 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9031 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9032 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9033 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9034 << new QgsStaticExpressionFunction( u"make_date"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"year"_s )
9035 << QgsExpressionFunction::Parameter( u"month"_s )
9036 << QgsExpressionFunction::Parameter( u"day"_s ),
9037 fcnMakeDate, u"Date and Time"_s )
9038 << new QgsStaticExpressionFunction( u"make_time"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s )
9039 << QgsExpressionFunction::Parameter( u"minute"_s )
9040 << QgsExpressionFunction::Parameter( u"second"_s ),
9041 fcnMakeTime, u"Date and Time"_s )
9042 << new QgsStaticExpressionFunction( u"make_datetime"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"year"_s )
9043 << QgsExpressionFunction::Parameter( u"month"_s )
9044 << QgsExpressionFunction::Parameter( u"day"_s )
9045 << QgsExpressionFunction::Parameter( u"hour"_s )
9046 << QgsExpressionFunction::Parameter( u"minute"_s )
9047 << QgsExpressionFunction::Parameter( u"second"_s ),
9048 fcnMakeDateTime, u"Date and Time"_s )
9049 << new QgsStaticExpressionFunction( u"make_interval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9050 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9051 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9052 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9053 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9054 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9055 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9056 fcnMakeInterval, u"Date and Time"_s )
9057 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9058 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9059 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9060 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9061 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9062 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9063 << new QgsStaticExpressionFunction( u"substr_count"_s, QgsExpressionFunction::ParameterList()
9064 << QgsExpressionFunction::Parameter( u"string"_s )
9065 << QgsExpressionFunction::Parameter( u"substring"_s )
9066 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9067 fcnSubstrCount,
9068 u"String"_s )
9069 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9070 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9071 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9072 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9073 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList()
9074 << QgsExpressionFunction::Parameter( u"string"_s )
9075 << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9076 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList()
9077 << QgsExpressionFunction::Parameter( u"string"_s )
9078 << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9079 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9080 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9081 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9082 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9083 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9084 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9085 << new QgsStaticExpressionFunction( u"wordwrap"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s ) << QgsExpressionFunction::Parameter( u"length"_s ) << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ), fcnWordwrap, u"String"_s )
9086 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9087 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9088 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s )}, fcnRepeat, u"String"_s )
9089 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9090 << new QgsStaticExpressionFunction( u"regexp_replace"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s )
9091 << QgsExpressionFunction::Parameter( u"replacement"_s ), fcnRegexpReplace, u"String"_s )
9092 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9093 << new QgsStaticExpressionFunction( u"substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ), fcnSubstr, u"String"_s, QString(),
9094 false, QSet< QString >(), false, QStringList(), true )
9095 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9096 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9097 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9098 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9099 << new QgsStaticExpressionFunction( u"rpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnRPad, u"String"_s )
9100 << new QgsStaticExpressionFunction( u"lpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnLPad, u"String"_s )
9101 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9102 << new QgsStaticExpressionFunction( u"format_number"_s, QgsExpressionFunction::ParameterList()
9103 << QgsExpressionFunction::Parameter( u"number"_s )
9104 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9105 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9106 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9107 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ), fcnFormatNumber, u"String"_s )
9108 << new QgsStaticExpressionFunction( u"format_date"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ) << QgsExpressionFunction::Parameter( u"format"_s ) << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ), fcnFormatDate, QStringList() << u"String"_s << u"Date and Time"_s )
9109 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9110 << new QgsStaticExpressionFunction( u"color_mix_rgb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s )
9111 << QgsExpressionFunction::Parameter( u"color2"_s )
9112 << QgsExpressionFunction::Parameter( u"ratio"_s ),
9113 fcnColorMixRgb, u"Color"_s )
9114 << new QgsStaticExpressionFunction( u"color_mix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s )
9115 << QgsExpressionFunction::Parameter( u"color2"_s )
9116 << QgsExpressionFunction::Parameter( u"ratio"_s ),
9117 fcnColorMix, u"Color"_s )
9118 << new QgsStaticExpressionFunction( u"color_rgb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"red"_s )
9119 << QgsExpressionFunction::Parameter( u"green"_s )
9120 << QgsExpressionFunction::Parameter( u"blue"_s ),
9121 fcnColorRgb, u"Color"_s )
9122 << new QgsStaticExpressionFunction( u"color_rgbf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"red"_s )
9123 << QgsExpressionFunction::Parameter( u"green"_s )
9124 << QgsExpressionFunction::Parameter( u"blue"_s )
9125 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9126 fcnColorRgbF, u"Color"_s )
9127 << new QgsStaticExpressionFunction( u"color_rgba"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"red"_s )
9128 << QgsExpressionFunction::Parameter( u"green"_s )
9129 << QgsExpressionFunction::Parameter( u"blue"_s )
9130 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9131 fncColorRgba, u"Color"_s )
9132 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s )
9133 << QgsExpressionFunction::Parameter( u"value"_s ),
9134 fcnRampColor, u"Color"_s )
9135 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s )
9136 << QgsExpressionFunction::Parameter( u"value"_s ),
9137 fcnRampColorObject, u"Color"_s )
9138 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s )
9139 << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ),
9140 fcnCreateRamp, u"Color"_s )
9141 << new QgsStaticExpressionFunction( u"color_hsl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9142 << QgsExpressionFunction::Parameter( u"saturation"_s )
9143 << QgsExpressionFunction::Parameter( u"lightness"_s ),
9144 fcnColorHsl, u"Color"_s )
9145 << new QgsStaticExpressionFunction( u"color_hsla"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9146 << QgsExpressionFunction::Parameter( u"saturation"_s )
9147 << QgsExpressionFunction::Parameter( u"lightness"_s )
9148 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9149 fncColorHsla, u"Color"_s )
9150 << new QgsStaticExpressionFunction( u"color_hslf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9151 << QgsExpressionFunction::Parameter( u"saturation"_s )
9152 << QgsExpressionFunction::Parameter( u"lightness"_s )
9153 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9154 fcnColorHslF, u"Color"_s )
9155 << new QgsStaticExpressionFunction( u"color_hsv"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9156 << QgsExpressionFunction::Parameter( u"saturation"_s )
9157 << QgsExpressionFunction::Parameter( u"value"_s ),
9158 fcnColorHsv, u"Color"_s )
9159 << new QgsStaticExpressionFunction( u"color_hsva"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9160 << QgsExpressionFunction::Parameter( u"saturation"_s )
9161 << QgsExpressionFunction::Parameter( u"value"_s )
9162 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9163 fncColorHsva, u"Color"_s )
9164 << new QgsStaticExpressionFunction( u"color_hsvf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s )
9165 << QgsExpressionFunction::Parameter( u"saturation"_s )
9166 << QgsExpressionFunction::Parameter( u"value"_s )
9167 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9168 fcnColorHsvF, u"Color"_s )
9169 << new QgsStaticExpressionFunction( u"color_cmyk"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"cyan"_s )
9170 << QgsExpressionFunction::Parameter( u"magenta"_s )
9171 << QgsExpressionFunction::Parameter( u"yellow"_s )
9172 << QgsExpressionFunction::Parameter( u"black"_s ),
9173 fcnColorCmyk, u"Color"_s )
9174 << new QgsStaticExpressionFunction( u"color_cmyka"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"cyan"_s )
9175 << QgsExpressionFunction::Parameter( u"magenta"_s )
9176 << QgsExpressionFunction::Parameter( u"yellow"_s )
9177 << QgsExpressionFunction::Parameter( u"black"_s )
9178 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9179 fncColorCmyka, u"Color"_s )
9180 << new QgsStaticExpressionFunction( u"color_cmykf"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"cyan"_s )
9181 << QgsExpressionFunction::Parameter( u"magenta"_s )
9182 << QgsExpressionFunction::Parameter( u"yellow"_s )
9183 << QgsExpressionFunction::Parameter( u"black"_s )
9184 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9185 fcnColorCmykF, u"Color"_s )
9186 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s )
9187 << QgsExpressionFunction::Parameter( u"component"_s ),
9188 fncColorPart, u"Color"_s )
9189 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s )
9190 << QgsExpressionFunction::Parameter( u"factor"_s ),
9191 fncDarker, u"Color"_s )
9192 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s )
9193 << QgsExpressionFunction::Parameter( u"factor"_s ),
9194 fncLighter, u"Color"_s )
9195 << new QgsStaticExpressionFunction( u"set_color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fncSetColorPart, u"Color"_s )
9196
9197 // file info
9198 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9199 fcnBaseFileName, u"Files and Paths"_s )
9200 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9201 fcnFileSuffix, u"Files and Paths"_s )
9202 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9203 fcnFileExists, u"Files and Paths"_s )
9204 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9205 fcnFileName, u"Files and Paths"_s )
9206 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9207 fcnPathIsFile, u"Files and Paths"_s )
9208 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9209 fcnPathIsDir, u"Files and Paths"_s )
9210 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9211 fcnFilePath, u"Files and Paths"_s )
9212 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9213 fcnFileSize, u"Files and Paths"_s )
9214
9215 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ),
9216 fcnExif, u"Files and Paths"_s )
9217 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ),
9218 fcnExifGeoTag, u"GeometryGroup"_s )
9219
9220 // hash
9221 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ),
9222 fcnGenericHash, u"Conversions"_s )
9223 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ),
9224 fcnHashMd5, u"Conversions"_s )
9225 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ),
9226 fcnHashSha256, u"Conversions"_s )
9227
9228 //base64
9229 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9230 fcnToBase64, u"Conversions"_s )
9231 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ),
9232 fcnFromBase64, u"Conversions"_s )
9233
9234 // magnetic models
9235 << new QgsStaticExpressionFunction( u"magnetic_declination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9236 << QgsExpressionFunction::Parameter( u"date"_s )
9237 << QgsExpressionFunction::Parameter( u"latitude"_s )
9238 << QgsExpressionFunction::Parameter( u"longitude"_s )
9239 << QgsExpressionFunction::Parameter( u"height"_s )
9240 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9241 fcnMagneticDeclination, u"MagneticModels"_s )
9242 << new QgsStaticExpressionFunction( u"magnetic_inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9243 << QgsExpressionFunction::Parameter( u"date"_s )
9244 << QgsExpressionFunction::Parameter( u"latitude"_s )
9245 << QgsExpressionFunction::Parameter( u"longitude"_s )
9246 << QgsExpressionFunction::Parameter( u"height"_s )
9247 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9248 fcnMagneticInclination, u"MagneticModels"_s )
9249 << new QgsStaticExpressionFunction( u"magnetic_declination_rate_of_change"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9250 << QgsExpressionFunction::Parameter( u"date"_s )
9251 << QgsExpressionFunction::Parameter( u"latitude"_s )
9252 << QgsExpressionFunction::Parameter( u"longitude"_s )
9253 << QgsExpressionFunction::Parameter( u"height"_s )
9254 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9255 fcnMagneticDeclinationRateOfChange, u"MagneticModels"_s )
9256 << new QgsStaticExpressionFunction( u"magnetic_inclination_rate_of_change"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"model_name"_s )
9257 << QgsExpressionFunction::Parameter( u"date"_s )
9258 << QgsExpressionFunction::Parameter( u"latitude"_s )
9259 << QgsExpressionFunction::Parameter( u"longitude"_s )
9260 << QgsExpressionFunction::Parameter( u"height"_s )
9261 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9262 fcnMagneticInclinationRateOfChange, u"MagneticModels"_s )
9263
9264 // deprecated stuff - hidden from users
9265 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9266
9267 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9268 geomFunc->setIsStatic( false );
9269 functions << geomFunc;
9270
9271 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9272 areaFunc->setIsStatic( false );
9273 functions << areaFunc;
9274
9275 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9276
9277 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9278 lengthFunc->setIsStatic( false );
9279 functions << lengthFunc;
9280
9281 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9282 perimeterFunc->setIsStatic( false );
9283 functions << perimeterFunc;
9284
9285 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9286
9287 functions << new QgsStaticExpressionFunction( u"roundness"_s,
9288 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9289 fcnRoundness, u"GeometryGroup"_s );
9290
9291 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9292 xFunc->setIsStatic( false );
9293 functions << xFunc;
9294
9295 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9296 yFunc->setIsStatic( false );
9297 functions << yFunc;
9298
9299 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9300 zFunc->setIsStatic( false );
9301 functions << zFunc;
9302
9303 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
9304 {
9305 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9306 { u"overlay_contains"_s, fcnGeomOverlayContains },
9307 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9308 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9309 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9310 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9311 { u"overlay_within"_s, fcnGeomOverlayWithin },
9312 };
9313 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9314 while ( i.hasNext() )
9315 {
9316 i.next();
9317 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction( i.key(), QgsExpressionFunction::ParameterList()
9318 << QgsExpressionFunction::Parameter( u"layer"_s )
9319 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9320 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9321 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9322 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9323 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9324 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9325 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9326 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false ),
9327 i.value(), u"GeometryGroup"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
9328
9329 // The current feature is accessed for the geometry, so this should not be cached
9330 fcnGeomOverlayFunc->setIsStatic( false );
9331 functions << fcnGeomOverlayFunc;
9332 }
9333
9334 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( u"overlay_nearest"_s, QgsExpressionFunction::ParameterList()
9335 << QgsExpressionFunction::Parameter( u"layer"_s )
9336 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9337 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9338 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9339 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9340 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9341 fcnGeomOverlayNearest, u"GeometryGroup"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
9342 // The current feature is accessed for the geometry, so this should not be cached
9343 fcnGeomOverlayNearestFunc->setIsStatic( false );
9344 functions << fcnGeomOverlayNearestFunc;
9345
9346 functions
9347 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9348 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9349 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
9350 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
9351 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
9352 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
9353 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
9354 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
9355 << new QgsStaticExpressionFunction( u"nodes_to_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9356 << QgsExpressionFunction::Parameter( u"ignore_closing_nodes"_s, true, false ),
9357 fcnNodesToPoints, u"GeometryGroup"_s )
9358 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
9359 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
9360 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
9361 << new QgsStaticExpressionFunction( u"make_point_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"x"_s )
9362 << QgsExpressionFunction::Parameter( u"y"_s )
9363 << QgsExpressionFunction::Parameter( u"m"_s ),
9364 fcnMakePointM, u"GeometryGroup"_s )
9365 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
9366 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
9367 << new QgsStaticExpressionFunction( u"make_triangle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s )
9368 << QgsExpressionFunction::Parameter( u"point2"_s )
9369 << QgsExpressionFunction::Parameter( u"point3"_s ),
9370 fcnMakeTriangle, u"GeometryGroup"_s )
9371 << new QgsStaticExpressionFunction( u"make_circle"_s, QgsExpressionFunction::ParameterList()
9372 << QgsExpressionFunction::Parameter( u"center"_s )
9373 << QgsExpressionFunction::Parameter( u"radius"_s )
9374 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9375 fcnMakeCircle, u"GeometryGroup"_s )
9376 << new QgsStaticExpressionFunction( u"make_ellipse"_s, QgsExpressionFunction::ParameterList()
9377 << QgsExpressionFunction::Parameter( u"center"_s )
9378 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
9379 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
9380 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9381 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9382 fcnMakeEllipse, u"GeometryGroup"_s )
9383 << new QgsStaticExpressionFunction( u"make_regular_polygon"_s, QgsExpressionFunction::ParameterList()
9384 << QgsExpressionFunction::Parameter( u"center"_s )
9385 << QgsExpressionFunction::Parameter( u"radius"_s )
9386 << QgsExpressionFunction::Parameter( u"number_sides"_s )
9387 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
9388 fcnMakeRegularPolygon, u"GeometryGroup"_s )
9389 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList()
9390 << QgsExpressionFunction::Parameter( u"point1"_s )
9391 << QgsExpressionFunction::Parameter( u"point2"_s ),
9392 fcnMakeSquare, u"GeometryGroup"_s )
9393 << new QgsStaticExpressionFunction( u"make_rectangle_3points"_s, QgsExpressionFunction::ParameterList()
9394 << QgsExpressionFunction::Parameter( u"point1"_s )
9395 << QgsExpressionFunction::Parameter( u"point2"_s )
9396 << QgsExpressionFunction::Parameter( u"point3"_s )
9397 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
9398 fcnMakeRectangleFrom3Points, u"GeometryGroup"_s )
9399 << new QgsStaticExpressionFunction( u"make_valid"_s, QgsExpressionFunction::ParameterList
9400 {
9401 QgsExpressionFunction::Parameter( u"geometry"_s ),
9402#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
9403 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
9404#else
9405 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
9406#endif
9407 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
9408 }, fcnGeomMakeValid, u"GeometryGroup"_s );
9409
9410 functions << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
9411 functions << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
9412 functions << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
9413 functions << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
9414
9415 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( u"$x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnOldXat, u"GeometryGroup"_s, QString(), true, QSet<QString>(), false, QStringList() << u"xat"_s );
9416 xAtFunc->setIsStatic( false );
9417 functions << xAtFunc;
9418
9419
9420 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( u"$y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnOldYat, u"GeometryGroup"_s, QString(), true, QSet<QString>(), false, QStringList() << u"yat"_s );
9421 yAtFunc->setIsStatic( false );
9422 functions << yAtFunc;
9423
9424 functions
9425 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
9426 << new QgsStaticExpressionFunction( u"x_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnXMin, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"xmin"_s )
9427 << new QgsStaticExpressionFunction( u"x_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnXMax, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"xmax"_s )
9428 << new QgsStaticExpressionFunction( u"y_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnYMin, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"ymin"_s )
9429 << new QgsStaticExpressionFunction( u"y_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnYMax, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"ymax"_s )
9430 << new QgsStaticExpressionFunction( u"geom_from_wkt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s ), fcnGeomFromWKT, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomFromWKT"_s )
9431 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
9432 << new QgsStaticExpressionFunction( u"geom_from_gml"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"gml"_s ), fcnGeomFromGML, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomFromGML"_s )
9433 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
9434 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
9435 << new QgsStaticExpressionFunction( u"intersects_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnBbox, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"bbox"_s )
9436 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9437 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9438 fcnDisjoint, u"GeometryGroup"_s )
9439 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9440 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9441 fcnIntersects, u"GeometryGroup"_s )
9442 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9443 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9444 fcnTouches, u"GeometryGroup"_s )
9445 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9446 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9447 fcnCrosses, u"GeometryGroup"_s )
9448 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9449 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9450 fcnContains, u"GeometryGroup"_s )
9451 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9452 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9453 fcnOverlaps, u"GeometryGroup"_s )
9454 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9455 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9456 fcnWithin, u"GeometryGroup"_s )
9457 << new QgsStaticExpressionFunction( u"translate"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9458 << QgsExpressionFunction::Parameter( u"dx"_s )
9459 << QgsExpressionFunction::Parameter( u"dy"_s ),
9460 fcnTranslate, u"GeometryGroup"_s )
9461 << new QgsStaticExpressionFunction( u"rotate"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9462 << QgsExpressionFunction::Parameter( u"rotation"_s )
9463 << QgsExpressionFunction::Parameter( u"center"_s, true )
9464 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
9465 fcnRotate, u"GeometryGroup"_s )
9466 << new QgsStaticExpressionFunction( u"scale"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9467 << QgsExpressionFunction::Parameter( u"x_scale"_s )
9468 << QgsExpressionFunction::Parameter( u"y_scale"_s )
9469 << QgsExpressionFunction::Parameter( u"center"_s, true ),
9470 fcnScale, u"GeometryGroup"_s )
9471 << new QgsStaticExpressionFunction( u"affine_transform"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9472 << QgsExpressionFunction::Parameter( u"delta_x"_s )
9473 << QgsExpressionFunction::Parameter( u"delta_y"_s )
9474 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
9475 << QgsExpressionFunction::Parameter( u"scale_x"_s )
9476 << QgsExpressionFunction::Parameter( u"scale_y"_s )
9477 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
9478 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
9479 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
9480 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
9481 fcnAffineTransform, u"GeometryGroup"_s )
9482 << new QgsStaticExpressionFunction( u"buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9483 << QgsExpressionFunction::Parameter( u"distance"_s )
9484 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
9485 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
9486 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
9487 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
9488 fcnBuffer, u"GeometryGroup"_s )
9489 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9490 fcnForceRHR, u"GeometryGroup"_s )
9491 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9492 fcnForcePolygonCW, u"GeometryGroup"_s )
9493 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9494 fcnForcePolygonCCW, u"GeometryGroup"_s )
9495 << new QgsStaticExpressionFunction( u"wedge_buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"center"_s )
9496 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9497 << QgsExpressionFunction::Parameter( u"width"_s )
9498 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
9499 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ), fcnWedgeBuffer, u"GeometryGroup"_s )
9500 << new QgsStaticExpressionFunction( u"tapered_buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9501 << QgsExpressionFunction::Parameter( u"start_width"_s )
9502 << QgsExpressionFunction::Parameter( u"end_width"_s )
9503 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9504 , fcnTaperedBuffer, u"GeometryGroup"_s )
9505 << new QgsStaticExpressionFunction( u"buffer_by_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9506 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9507 , fcnBufferByM, u"GeometryGroup"_s )
9508 << new QgsStaticExpressionFunction( u"offset_curve"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9509 << QgsExpressionFunction::Parameter( u"distance"_s )
9510 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9511 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
9512 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
9513 fcnOffsetCurve, u"GeometryGroup"_s )
9514 << new QgsStaticExpressionFunction( u"single_sided_buffer"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9515 << QgsExpressionFunction::Parameter( u"distance"_s )
9516 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9517 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
9518 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
9519 fcnSingleSidedBuffer, u"GeometryGroup"_s )
9520 << new QgsStaticExpressionFunction( u"extend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9521 << QgsExpressionFunction::Parameter( u"start_distance"_s )
9522 << QgsExpressionFunction::Parameter( u"end_distance"_s ),
9523 fcnExtend, u"GeometryGroup"_s )
9524 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
9525 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
9526 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9527 << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
9528 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
9529 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
9530 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9531 << QgsExpressionFunction::Parameter( u"index"_s ),
9532 fcnInteriorRingN, u"GeometryGroup"_s )
9533 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9534 << QgsExpressionFunction::Parameter( u"index"_s ),
9535 fcnGeometryN, u"GeometryGroup"_s )
9536 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
9537 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
9538 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList
9539 {
9540 QgsExpressionFunction::Parameter( u"geometry1"_s ),
9541 QgsExpressionFunction::Parameter( u"geometry2"_s )
9542 }, fcnSharedPaths, u"GeometryGroup"_s )
9543 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
9544 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
9545 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
9546 << new QgsStaticExpressionFunction( u"smooth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
9547 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
9548 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
9549 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ), fcnSmooth, u"GeometryGroup"_s )
9550 << new QgsStaticExpressionFunction( u"triangular_wave"_s,
9551 {
9552 QgsExpressionFunction::Parameter( u"geometry"_s ),
9553 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9554 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9555 QgsExpressionFunction::Parameter( u"strict"_s, true, false )
9556 }, fcnTriangularWave, u"GeometryGroup"_s )
9557 << new QgsStaticExpressionFunction( u"triangular_wave_randomized"_s,
9558 {
9559 QgsExpressionFunction::Parameter( u"geometry"_s ),
9560 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9561 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9562 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9563 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9564 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
9565 }, fcnTriangularWaveRandomized, u"GeometryGroup"_s )
9566 << new QgsStaticExpressionFunction( u"square_wave"_s,
9567 {
9568 QgsExpressionFunction::Parameter( u"geometry"_s ),
9569 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9570 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9571 QgsExpressionFunction::Parameter( u"strict"_s, true, false )
9572 }, fcnSquareWave, u"GeometryGroup"_s )
9573 << new QgsStaticExpressionFunction( u"square_wave_randomized"_s,
9574 {
9575 QgsExpressionFunction::Parameter( u"geometry"_s ),
9576 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9577 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9578 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9579 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9580 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
9581 }, fcnSquareWaveRandomized, u"GeometryGroup"_s )
9582 << new QgsStaticExpressionFunction( u"wave"_s,
9583 {
9584 QgsExpressionFunction::Parameter( u"geometry"_s ),
9585 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9586 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9587 QgsExpressionFunction::Parameter( u"strict"_s, true, false )
9588 }, fcnRoundWave, u"GeometryGroup"_s )
9589 << new QgsStaticExpressionFunction( u"wave_randomized"_s,
9590 {
9591 QgsExpressionFunction::Parameter( u"geometry"_s ),
9592 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9593 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9594 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9595 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9596 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
9597 }, fcnRoundWaveRandomized, u"GeometryGroup"_s )
9598 << new QgsStaticExpressionFunction( u"apply_dash_pattern"_s,
9599 {
9600 QgsExpressionFunction::Parameter( u"geometry"_s ),
9601 QgsExpressionFunction::Parameter( u"pattern"_s ),
9602 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
9603 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
9604 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
9605 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
9606 }, fcnApplyDashPattern, u"GeometryGroup"_s )
9607 << new QgsStaticExpressionFunction( u"densify_by_count"_s,
9608 {
9609 QgsExpressionFunction::Parameter( u"geometry"_s ),
9610 QgsExpressionFunction::Parameter( u"vertices"_s )
9611 }, fcnDensifyByCount, u"GeometryGroup"_s )
9612 << new QgsStaticExpressionFunction( u"densify_by_distance"_s,
9613 {
9614 QgsExpressionFunction::Parameter( u"geometry"_s ),
9615 QgsExpressionFunction::Parameter( u"distance"_s )
9616 }, fcnDensifyByDistance, u"GeometryGroup"_s )
9617 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
9618 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
9619 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
9620 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
9621 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
9622 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
9623 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
9624 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
9625 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
9626 << new QgsStaticExpressionFunction( u"is_empty_or_null"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmptyOrNull, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9627 << new QgsStaticExpressionFunction( u"convex_hull"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnConvexHull, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"convexHull"_s )
9628#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9629 << new QgsStaticExpressionFunction( u"concave_hull"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9630 << QgsExpressionFunction::Parameter( u"target_percent"_s )
9631 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ), fcnConcaveHull, u"GeometryGroup"_s )
9632#endif
9633 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList()
9634 << QgsExpressionFunction::Parameter( u"geometry"_s ),
9635 fcnOrientedBBox, u"GeometryGroup"_s )
9636 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList()
9637 << QgsExpressionFunction::Parameter( u"geometry"_s ),
9638 fcnMainAngle, u"GeometryGroup"_s )
9639 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList()
9640 << QgsExpressionFunction::Parameter( u"geometry"_s )
9641 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9642 fcnMinimalCircle, u"GeometryGroup"_s )
9643 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9644 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9645 fcnDifference, u"GeometryGroup"_s )
9646 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9647 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9648 fcnDistance, u"GeometryGroup"_s )
9649 << new QgsStaticExpressionFunction( u"hausdorff_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s )
9650 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
9651 fcnHausdorffDistance, u"GeometryGroup"_s )
9652 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9653 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9654 fcnIntersection, u"GeometryGroup"_s )
9655 << new QgsStaticExpressionFunction( u"sym_difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9656 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9657 fcnSymDifference, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"symDifference"_s )
9658 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9659 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9660 fcnCombine, u"GeometryGroup"_s )
9661 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9662 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9663 fcnCombine, u"GeometryGroup"_s )
9664 << new QgsStaticExpressionFunction( u"geom_to_wkt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9665 << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
9666 fcnGeomToWKT, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomToWKT"_s )
9667 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9668 fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
9669 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
9670 << new QgsStaticExpressionFunction( u"transform"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9671 << QgsExpressionFunction::Parameter( u"source_auth_id"_s )
9672 << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
9673 fcnTransformGeometry, u"GeometryGroup"_s )
9674 << new QgsStaticExpressionFunction( u"extrude"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9675 << QgsExpressionFunction::Parameter( u"x"_s )
9676 << QgsExpressionFunction::Parameter( u"y"_s ),
9677 fcnExtrude, u"GeometryGroup"_s, QString() )
9678 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9679 fcnGeomIsMultipart, u"GeometryGroup"_s )
9680 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9681 fcnZMax, u"GeometryGroup"_s )
9682 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9683 fcnZMin, u"GeometryGroup"_s )
9684 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9685 fcnMMax, u"GeometryGroup"_s )
9686 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9687 fcnMMin, u"GeometryGroup"_s )
9688 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9689 fcnSinuosity, u"GeometryGroup"_s )
9690 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9691 fcnStraightDistance2d, u"GeometryGroup"_s );
9692
9693
9694 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( u"order_parts"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9695 << QgsExpressionFunction::Parameter( u"orderby"_s )
9696 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
9697 fcnOrderParts, u"GeometryGroup"_s, QString() );
9698
9699 orderPartsFunc->setIsStaticFunction(
9700 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9701 {
9702 const QList< QgsExpressionNode *> argList = node->args()->list();
9703 for ( QgsExpressionNode *argNode : argList )
9704 {
9705 if ( !argNode->isStatic( parent, context ) )
9706 return false;
9707 }
9708
9709 if ( node->args()->count() > 1 )
9710 {
9711 QgsExpressionNode *argNode = node->args()->at( 1 );
9712
9713 QString expString = argNode->eval( parent, context ).toString();
9714
9715 QgsExpression e( expString );
9716
9717 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9718 return true;
9719 }
9720
9721 return true;
9722 } );
9723
9724 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9725 {
9726 if ( node->args()->count() > 1 )
9727 {
9728 QgsExpressionNode *argNode = node->args()->at( 1 );
9729 QString expression = argNode->eval( parent, context ).toString();
9731 e.prepare( context );
9732 context->setCachedValue( expression, QVariant::fromValue( e ) );
9733 }
9734 return true;
9735 }
9736 );
9737 functions << orderPartsFunc;
9738
9739 functions
9740 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9741 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9742 fcnClosestPoint, u"GeometryGroup"_s )
9743 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s )
9744 << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9745 fcnShortestLine, u"GeometryGroup"_s )
9746 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9747 << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
9748 << new QgsStaticExpressionFunction( u"line_interpolate_point_by_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9749 << QgsExpressionFunction::Parameter( u"m"_s ) << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
9750 fcnLineInterpolatePointByM, u"GeometryGroup"_s )
9751 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9752 << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
9753 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9754 << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
9755 << new QgsStaticExpressionFunction( u"line_locate_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9756 << QgsExpressionFunction::Parameter( u"m"_s ) << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
9757 fcnLineLocateM, u"GeometryGroup"_s )
9758 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9759 << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
9760 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9761 << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
9762 << new QgsStaticExpressionFunction( u"line_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s )
9763 << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ), fcnLineSubset, u"GeometryGroup"_s );
9764
9765
9766 // **Record** functions
9767
9768 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
9769 idFunc->setIsStatic( false );
9770 functions << idFunc;
9771
9772 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
9773 currentFeatureFunc->setIsStatic( false );
9774 functions << currentFeatureFunc;
9775
9776 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( u"uuid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ), fcnUuid, u"Record and Attributes"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$uuid"_s );
9777 uuidFunc->setIsStatic( false );
9778 functions << uuidFunc;
9779
9780 functions
9781 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
9782 << new QgsStaticExpressionFunction( u"get_feature"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9783 << QgsExpressionFunction::Parameter( u"attribute"_s )
9784 << QgsExpressionFunction::Parameter( u"value"_s, true ),
9785 fcnGetFeature, u"Record and Attributes"_s, QString(), false, QSet<QString>(), false, QStringList() << u"QgsExpressionUtils::getFeature"_s )
9786 << new QgsStaticExpressionFunction( u"get_feature_by_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9787 << QgsExpressionFunction::Parameter( u"feature_id"_s ),
9788 fcnGetFeatureById, u"Record and Attributes"_s, QString(), false, QSet<QString>(), false );
9789
9790 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( u"attributes"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
9791 fcnAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9792 attributesFunc->setIsStatic( false );
9793 functions << attributesFunc;
9794 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1,
9795 fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9796 representAttributesFunc->setIsStatic( false );
9797 functions << representAttributesFunc;
9798
9799 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( u"is_feature_valid"_s,
9800 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s, true )
9801 << QgsExpressionFunction::Parameter( u"feature"_s, true )
9802 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
9803 fcnValidateFeature, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9804 validateFeature->setIsStatic( false );
9805 functions << validateFeature;
9806
9807 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( u"is_attribute_valid"_s,
9808 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"attribute"_s, false )
9809 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s, true )
9810 << QgsExpressionFunction::Parameter( u"feature"_s, true )
9811 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
9812 fcnValidateAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9813 validateAttribute->setIsStatic( false );
9814 functions << validateAttribute;
9815
9816 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction(
9817 u"maptip"_s,
9818 -1,
9819 fcnFeatureMaptip,
9820 u"Record and Attributes"_s,
9821 QString(),
9822 false,
9823 QSet<QString>()
9824 );
9825 maptipFunc->setIsStatic( false );
9826 functions << maptipFunc;
9827
9828 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction(
9829 u"display_expression"_s,
9830 -1,
9831 fcnFeatureDisplayExpression,
9832 u"Record and Attributes"_s,
9833 QString(),
9834 false,
9835 QSet<QString>()
9836 );
9837 displayFunc->setIsStatic( false );
9838 functions << displayFunc;
9839
9840 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction(
9841 u"is_selected"_s,
9842 -1,
9843 fcnIsSelected,
9844 u"Record and Attributes"_s,
9845 QString(),
9846 false,
9847 QSet<QString>()
9848 );
9849 isSelectedFunc->setIsStatic( false );
9850 functions << isSelectedFunc;
9851
9852 functions
9853 << new QgsStaticExpressionFunction(
9854 u"num_selected"_s,
9855 -1,
9856 fcnNumSelected,
9857 u"Record and Attributes"_s,
9858 QString(),
9859 false,
9860 QSet<QString>()
9861 );
9862
9863 functions
9864 << new QgsStaticExpressionFunction(
9865 u"sqlite_fetch_and_increment"_s,
9867 << QgsExpressionFunction::Parameter( u"database"_s )
9868 << QgsExpressionFunction::Parameter( u"table"_s )
9869 << QgsExpressionFunction::Parameter( u"id_field"_s )
9870 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
9871 << QgsExpressionFunction::Parameter( u"filter_value"_s )
9872 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
9873 fcnSqliteFetchAndIncrement,
9874 u"Record and Attributes"_s
9875 );
9876
9877 // **CRS** functions
9878 functions
9879 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
9880 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
9881
9882
9883 // **Fields and Values** functions
9884 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( u"represent_value"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"field_name"_s, true ), fcnRepresentValue, u"Record and Attributes"_s );
9885
9886 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9887 {
9888 Q_UNUSED( context )
9889 if ( node->args()->count() == 1 )
9890 {
9891 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9892 if ( colRef )
9893 {
9894 return true;
9895 }
9896 else
9897 {
9898 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9899 return false;
9900 }
9901 }
9902 else if ( node->args()->count() == 2 )
9903 {
9904 return true;
9905 }
9906 else
9907 {
9908 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9909 return false;
9910 }
9911 }
9912 );
9913
9914 functions << representValueFunc;
9915
9916 // **General** functions
9917 functions
9918 << new QgsStaticExpressionFunction( u"layer_property"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9919 << QgsExpressionFunction::Parameter( u"property"_s ),
9920 fcnGetLayerProperty, u"Map Layers"_s )
9921 << new QgsStaticExpressionFunction( u"decode_uri"_s,
9923 << QgsExpressionFunction::Parameter( u"layer"_s )
9924 << QgsExpressionFunction::Parameter( u"part"_s, true ),
9925 fcnDecodeUri, u"Map Layers"_s )
9926 << new QgsStaticExpressionFunction( u"mime_type"_s,
9928 << QgsExpressionFunction::Parameter( u"binary_data"_s ),
9929 fcnMimeType, u"General"_s )
9930 << new QgsStaticExpressionFunction( u"raster_statistic"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s )
9931 << QgsExpressionFunction::Parameter( u"band"_s )
9932 << QgsExpressionFunction::Parameter( u"statistic"_s ), fcnGetRasterBandStat, u"Rasters"_s );
9933
9934 // **var** function
9935 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
9936 varFunction->setIsStaticFunction(
9937 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9938 {
9939 /* A variable node is static if it has a static name and the name can be found at prepare
9940 * time and is tagged with isStatic.
9941 * It is not static if a variable is set during iteration or not tagged isStatic.
9942 * (e.g. geom_part variable)
9943 */
9944 if ( node->args()->count() > 0 )
9945 {
9946 QgsExpressionNode *argNode = node->args()->at( 0 );
9947
9948 if ( !argNode->isStatic( parent, context ) )
9949 return false;
9950
9951 const QString varName = argNode->eval( parent, context ).toString();
9952 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
9953 return false;
9954
9955 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9956 return scope ? scope->isStatic( varName ) : false;
9957 }
9958 return false;
9959 }
9960 );
9961 varFunction->setUsesGeometryFunction(
9962 []( const QgsExpressionNodeFunction * node ) -> bool
9963 {
9964 if ( node && node->args()->count() > 0 )
9965 {
9966 QgsExpressionNode *argNode = node->args()->at( 0 );
9967 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9968 {
9969 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
9970 return true;
9971 }
9972 }
9973 return false;
9974 }
9975 );
9976
9977 functions
9978 << varFunction;
9979
9980 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9981 evalTemplateFunction->setIsStaticFunction(
9982 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9983 {
9984 if ( node->args()->count() > 0 )
9985 {
9986 QgsExpressionNode *argNode = node->args()->at( 0 );
9987
9988 if ( argNode->isStatic( parent, context ) )
9989 {
9990 QString expString = argNode->eval( parent, context ).toString();
9991
9992 QgsExpression e( expString );
9993
9994 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9995 return true;
9996 }
9997 }
9998
9999 return false;
10000 } );
10001 functions << evalTemplateFunction;
10002
10003 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10004 evalFunc->setIsStaticFunction(
10005 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
10006 {
10007 if ( node->args()->count() > 0 )
10008 {
10009 QgsExpressionNode *argNode = node->args()->at( 0 );
10010
10011 if ( argNode->isStatic( parent, context ) )
10012 {
10013 QString expString = argNode->eval( parent, context ).toString();
10014
10015 QgsExpression e( expString );
10016
10017 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10018 return true;
10019 }
10020 }
10021
10022 return false;
10023 } );
10024
10025 functions << evalFunc;
10026
10027 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10028 attributeFunc->setIsStaticFunction(
10029 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
10030 {
10031 const QList< QgsExpressionNode *> argList = node->args()->list();
10032 for ( QgsExpressionNode *argNode : argList )
10033 {
10034 if ( !argNode->isStatic( parent, context ) )
10035 return false;
10036 }
10037
10038 if ( node->args()->count() == 1 )
10039 {
10040 // not static -- this is the variant which uses the current feature taken direct from the expression context
10041 return false;
10042 }
10043
10044 return true;
10045 } );
10046 functions << attributeFunc;
10047
10048 functions
10049 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10050 << new QgsWithVariableExpressionFunction()
10051 << new QgsStaticExpressionFunction( u"raster_value"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnRasterValue, u"Rasters"_s )
10052 << new QgsStaticExpressionFunction( u"raster_attributes"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnRasterAttributes, u"Rasters"_s )
10053
10054 // functions for arrays
10055 << new QgsArrayForeachExpressionFunction()
10056 << new QgsArrayFilterExpressionFunction()
10057 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10058 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10059 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10060 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10061 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10062 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array_a"_s ) << QgsExpressionFunction::Parameter( u"array_b"_s ), fcnArrayAll, u"Arrays"_s )
10063 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10064 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10065 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10066 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10067 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10068 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10069 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10070 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10071 << new QgsStaticExpressionFunction( u"array_majority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMajority, u"Arrays"_s )
10072 << new QgsStaticExpressionFunction( u"array_minority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMinority, u"Arrays"_s )
10073 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10074 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10075 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10076 << new QgsStaticExpressionFunction( u"array_insert"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayInsert, u"Arrays"_s )
10077 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10078 << new QgsStaticExpressionFunction( u"array_remove_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayRemoveAll, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10079 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10080 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"array_prioritize"_s ), fcnArrayPrioritize, u"Arrays"_s )
10081 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10082 << new QgsStaticExpressionFunction( u"array_slice"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ), fcnArraySlice, u"Arrays"_s )
10083 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10084 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10085 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10086 << new QgsStaticExpressionFunction( u"array_to_string"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," ) << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ), fcnArrayToString, u"Arrays"_s )
10087 << new QgsStaticExpressionFunction( u"string_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," ) << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ), fcnStringToArray, u"Arrays"_s )
10088 << new QgsStaticExpressionFunction( u"generate_series"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ), fcnGenerateSeries, u"Arrays"_s )
10089 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10090
10091 //functions for maps
10092 << new QgsStaticExpressionFunction( u"from_json"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLoadJson, u"Maps"_s, QString(), false, QSet<QString>(), false, QStringList() << u"json_to_map"_s )
10093 << new QgsStaticExpressionFunction( u"to_json"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"json_string"_s ), fcnWriteJson, u"Maps"_s, QString(), false, QSet<QString>(), false, QStringList() << u"map_to_json"_s )
10094 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10095 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10096 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10097 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10098 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10099 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10100 << new QgsStaticExpressionFunction( u"map_insert"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnMapInsert, u"Maps"_s )
10101 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10102 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10103 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10104 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s )
10105 << QgsExpressionFunction::Parameter( u"prefix"_s ),
10106 fcnMapPrefixKeys, u"Maps"_s )
10107 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ),
10108 fcnMapToHtmlTable, u"Maps"_s )
10109 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ),
10110 fcnMapToHtmlDefinitionList, u"Maps"_s )
10111 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ),
10112 fcnToFormUrlEncode, u"Maps"_s )
10113
10114 ;
10115
10117
10118 //QgsExpression has ownership of all built-in functions
10119 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10120 {
10121 *sOwnedFunctions() << func;
10122 *sBuiltinFunctions() << func->name();
10123 sBuiltinFunctions()->append( func->aliases() );
10124 }
10125 }
10126 return functions;
10127}
10128
10129bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10130{
10131 int fnIdx = functionIndex( function->name() );
10132 if ( fnIdx != -1 )
10133 {
10134 return false;
10135 }
10136
10137 QMutexLocker locker( &sFunctionsMutex );
10138 sFunctions()->append( function );
10139 if ( transferOwnership )
10140 sOwnedFunctions()->append( function );
10141
10142 return true;
10143}
10144
10145bool QgsExpression::unregisterFunction( const QString &name )
10146{
10147 // You can never override the built in functions.
10148 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10149 {
10150 return false;
10151 }
10152 int fnIdx = functionIndex( name );
10153 if ( fnIdx != -1 )
10154 {
10155 QMutexLocker locker( &sFunctionsMutex );
10156 sFunctions()->removeAt( fnIdx );
10157 sFunctionIndexMap.clear();
10158 return true;
10159 }
10160 return false;
10161}
10162
10164{
10165 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10166 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10167 {
10168 sBuiltinFunctions()->removeAll( func->name() );
10169 for ( const QString &alias : func->aliases() )
10171 sBuiltinFunctions()->removeAll( alias );
10172 }
10173
10174 sFunctions()->removeAll( func );
10175 }
10176
10177 qDeleteAll( *sOwnedFunctions() );
10178 sOwnedFunctions()->clear();
10180
10181const QStringList &QgsExpression::BuiltinFunctions()
10182{
10183 if ( sBuiltinFunctions()->isEmpty() )
10184 {
10185 Functions(); // this method builds the gmBuiltinFunctions as well
10186 }
10187 return *sBuiltinFunctions();
10189
10191 : QgsExpressionFunction( u"array_foreach"_s, QgsExpressionFunction::ParameterList() // skip-keyword-check
10192 << QgsExpressionFunction::Parameter( u"array"_s )
10193 << QgsExpressionFunction::Parameter( u"expression"_s ),
10194 u"Arrays"_s )
10195{
10196
10197}
10198
10200{
10201 bool isStatic = false;
10202
10203 QgsExpressionNode::NodeList *args = node->args();
10205 if ( args->count() < 2 )
10206 return false;
10207
10208 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10209 {
10210 isStatic = true;
10211 }
10212 return isStatic;
10213}
10214
10216{
10217 Q_UNUSED( node )
10218 QVariantList result;
10219
10220 if ( args->count() < 2 )
10221 // error
10222 return result;
10223
10224 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10225
10226 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10227 std::unique_ptr< QgsExpressionContext > tempContext;
10228 if ( !subContext )
10229 {
10230 tempContext = std::make_unique< QgsExpressionContext >();
10231 subContext = tempContext.get();
10232 }
10233
10234 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10235 subContext->appendScope( subScope );
10236
10237 int i = 0;
10238 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
10239 {
10240 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
10241 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
10242 result << args->at( 1 )->eval( parent, subContext );
10243 }
10244
10245 if ( context )
10246 delete subContext->popScope();
10247
10248 return result;
10249}
10250
10251QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10253 // This is a dummy function, all the real handling is in run
10254 Q_UNUSED( values )
10255 Q_UNUSED( context )
10256 Q_UNUSED( parent )
10257 Q_UNUSED( node )
10258
10259 Q_ASSERT( false );
10260 return QVariant();
10261}
10262
10264{
10265 QgsExpressionNode::NodeList *args = node->args();
10266
10267 if ( args->count() < 2 )
10268 // error
10269 return false;
10270
10271 args->at( 0 )->prepare( parent, context );
10272
10273 QgsExpressionContext subContext;
10274 if ( context )
10275 subContext = *context;
10278 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
10279 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
10280 subContext.appendScope( subScope );
10281
10282 args->at( 1 )->prepare( parent, &subContext );
10283
10284 return true;
10285}
10289 << QgsExpressionFunction::Parameter( u"array"_s )
10290 << QgsExpressionFunction::Parameter( u"expression"_s )
10291 << QgsExpressionFunction::Parameter( u"limit"_s, true, 0 ),
10292 u"Arrays"_s )
10293{
10294
10295}
10296
10298{
10299 bool isStatic = false;
10300
10301 QgsExpressionNode::NodeList *args = node->args();
10303 if ( args->count() < 2 )
10304 return false;
10305
10306 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10307 {
10308 isStatic = true;
10309 }
10310 return isStatic;
10311}
10312
10314{
10315 Q_UNUSED( node )
10316 QVariantList result;
10317
10318 if ( args->count() < 2 )
10319 // error
10320 return result;
10321
10322 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10323
10324 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10325 std::unique_ptr< QgsExpressionContext > tempContext;
10326 if ( !subContext )
10327 {
10328 tempContext = std::make_unique< QgsExpressionContext >();
10329 subContext = tempContext.get();
10330 }
10331
10332 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10333 subContext->appendScope( subScope );
10334
10335 int limit = 0;
10336 if ( args->count() >= 3 )
10337 {
10338 const QVariant limitVar = args->at( 2 )->eval( parent, context );
10339
10340 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
10341 {
10342 limit = limitVar.toInt();
10343 }
10344 else
10345 {
10346 return result;
10347 }
10348 }
10349
10350 for ( const QVariant &value : array )
10351 {
10352 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
10353 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
10354 {
10355 result << value;
10356
10357 if ( limit > 0 && limit == result.size() )
10358 break;
10359 }
10360 }
10361
10362 if ( context )
10363 delete subContext->popScope();
10364
10365 return result;
10366}
10367
10368QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10370 // This is a dummy function, all the real handling is in run
10371 Q_UNUSED( values )
10372 Q_UNUSED( context )
10373 Q_UNUSED( parent )
10374 Q_UNUSED( node )
10375
10376 Q_ASSERT( false );
10377 return QVariant();
10378}
10379
10381{
10382 QgsExpressionNode::NodeList *args = node->args();
10383
10384 if ( args->count() < 2 )
10385 // error
10386 return false;
10387
10388 args->at( 0 )->prepare( parent, context );
10389
10390 QgsExpressionContext subContext;
10391 if ( context )
10392 subContext = *context;
10393
10395 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
10396 subContext.appendScope( subScope );
10397
10398 args->at( 1 )->prepare( parent, &subContext );
10399
10400 return true;
10403 : QgsExpressionFunction( u"with_variable"_s, QgsExpressionFunction::ParameterList() <<
10404 QgsExpressionFunction::Parameter( u"name"_s )
10405 << QgsExpressionFunction::Parameter( u"value"_s )
10406 << QgsExpressionFunction::Parameter( u"expression"_s ),
10407 u"General"_s )
10408{
10409
10410}
10411
10413{
10414 bool isStatic = false;
10415
10416 QgsExpressionNode::NodeList *args = node->args();
10417
10418 if ( args->count() < 3 )
10419 return false;
10420
10421 // We only need to check if the node evaluation is static, if both - name and value - are static.
10422 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10423 {
10424 QVariant name = args->at( 0 )->eval( parent, context );
10425 QVariant value = args->at( 1 )->eval( parent, context );
10427 // Temporarily append a new scope to provide the variable
10428 appendTemporaryVariable( context, name.toString(), value );
10429 if ( args->at( 2 )->isStatic( parent, context ) )
10430 isStatic = true;
10431 popTemporaryVariable( context );
10432 }
10433
10434 return isStatic;
10435}
10436
10438{
10439 Q_UNUSED( node )
10440 QVariant result;
10441
10442 if ( args->count() < 3 )
10443 // error
10444 return result;
10445
10446 QVariant name = args->at( 0 )->eval( parent, context );
10447 QVariant value = args->at( 1 )->eval( parent, context );
10448
10449 const QgsExpressionContext *updatedContext = context;
10450 std::unique_ptr< QgsExpressionContext > tempContext;
10451 if ( !updatedContext )
10452 {
10453 tempContext = std::make_unique< QgsExpressionContext >();
10454 updatedContext = tempContext.get();
10456
10457 appendTemporaryVariable( updatedContext, name.toString(), value );
10458 result = args->at( 2 )->eval( parent, updatedContext );
10459
10460 if ( context )
10461 popTemporaryVariable( updatedContext );
10462
10463 return result;
10464}
10465
10466QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10468 // This is a dummy function, all the real handling is in run
10469 Q_UNUSED( values )
10470 Q_UNUSED( context )
10471 Q_UNUSED( parent )
10472 Q_UNUSED( node )
10473
10474 Q_ASSERT( false );
10475 return QVariant();
10476}
10477
10479{
10480 QgsExpressionNode::NodeList *args = node->args();
10481
10482 if ( args->count() < 3 )
10483 // error
10484 return false;
10485
10486 QVariant name = args->at( 0 )->prepare( parent, context );
10487 QVariant value = args->at( 1 )->prepare( parent, context );
10488
10489 const QgsExpressionContext *updatedContext = context;
10490 std::unique_ptr< QgsExpressionContext > tempContext;
10491 if ( !updatedContext )
10492 {
10493 tempContext = std::make_unique< QgsExpressionContext >();
10494 updatedContext = tempContext.get();
10495 }
10496
10497 appendTemporaryVariable( updatedContext, name.toString(), value );
10498 args->at( 2 )->prepare( parent, updatedContext );
10499
10500 if ( context )
10501 popTemporaryVariable( updatedContext );
10502
10503 return true;
10504}
10505
10506void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10507{
10508 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10509 delete updatedContext->popScope();
10510}
10511
10512void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10513{
10514 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
10515 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
10516
10517 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10518 updatedContext->appendScope( scope );
10519}
@ Left
Buffer to left of line.
Definition qgis.h:2143
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3338
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3340
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3339
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3341
@ Success
Operation succeeded.
Definition qgis.h:2089
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3062
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2242
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
@ Unknown
Unknown types.
Definition qgis.h:369
@ Null
No geometry.
Definition qgis.h:370
JoinStyle
Join styles for buffers.
Definition qgis.h:2167
@ Bevel
Use beveled joins.
Definition qgis.h:2170
@ Round
Use rounded joins.
Definition qgis.h:2168
@ Miter
Use mitered joins.
Definition qgis.h:2169
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6193
@ StdDev
Standard deviation.
Definition qgis.h:6200
@ NoStatistic
No statistic.
Definition qgis.h:6194
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:201
@ Plugin
Plugin based layer.
Definition qgis.h:196
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:202
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:199
@ Vector
Vector layer.
Definition qgis.h:194
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:197
@ Raster
Raster layer.
Definition qgis.h:195
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:200
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2154
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2156
@ Round
Round cap.
Definition qgis.h:2155
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2157
Aggregate
Available aggregates to calculate.
Definition qgis.h:6070
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6087
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6084
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6077
@ Median
Median of values (numeric fields only).
Definition qgis.h:6078
@ Max
Max of values.
Definition qgis.h:6075
@ Min
Min of values.
Definition qgis.h:6074
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6088
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6081
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6092
@ Sum
Sum of values.
Definition qgis.h:6076
@ Minority
Minority of values.
Definition qgis.h:6082
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6073
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6091
@ Majority
Majority of values.
Definition qgis.h:6083
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6080
@ Count
Count.
Definition qgis.h:6071
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6085
@ CountDistinct
Number of distinct values.
Definition qgis.h:6072
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6089
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6090
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6086
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3323
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3326
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3328
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3327
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3325
@ NoRule
No special rule.
Definition qgis.h:3324
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2226
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2227
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2228
@ Point
Point.
Definition qgis.h:282
@ PointM
PointM.
Definition qgis.h:315
@ PointZ
PointZ.
Definition qgis.h:299
@ GeometryCollection
GeometryCollection.
Definition qgis.h:289
@ PointZM
PointZM.
Definition qgis.h:331
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:44
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:278
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:176
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:54
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:273
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:39
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
An abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
QgsExpressionFunction(const QString &fnname, int params, const QString &group, const QString &helpText=QString(), bool lazyEval=false, bool handlesNull=false, bool isContextual=false)
Constructor for function which uses unnamed parameters.
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
const QString helpText() const
The help text for the function.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes its value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions).
friend class QgsExpressionNodeFunction
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsExpression(const QString &expr)
Creates a new expression based on the provided string.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:63
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:747
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:141
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2803
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
A representation of the interval between two datetime values.
Definition qgsinterval.h:47
bool isValid() const
Returns true if the interval is valid.
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
double years() const
Returns the interval duration in years (based on an average year length).
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Represents a model of the Earth's magnetic field.
static bool fieldComponentsWithTimeDerivatives(double Bx, double By, double Bz, double Bxt, double Byt, double Bzt, double &H, double &F, double &D, double &I, double &Ht, double &Ft, double &Dt, double &It)
Compute various quantities dependent on a magnetic field and their rates of change.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:80
QString name
Definition qgsmaplayer.h:84
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:83
QgsLayerMetadata metadata
Definition qgsmaplayer.h:86
Qgis::LayerType type
Definition qgsmaplayer.h:90
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString mapTipTemplate
Definition qgsmaplayer.h:93
Implementation of a geometry simplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:695
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:561
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:429
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:108
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:707
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:120
static QgsProject * instance()
Returns the QgsProject singleton instance.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
bool isRamp() const
Returns true if the field carries a color ramp component information (RedMin/RedMax,...
bool isColor() const
Returns true if the field carries a color component (Red, Green, Blue and optionally Alpha) informati...
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
void grow(double delta)
Grows the rectangle in place by the specified amount.
double yMaximum
QgsPointXY center
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices).
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
Q_INVOKABLE QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString unaccent(const QString &input)
Removes accents and other diacritical marks from a string, replacing accented characters with their u...
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance).
Definition qgsstyle.cpp:502
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:147
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:592
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7439
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7438
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6929
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6888
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
const QString cacheKey(const QString &pathIn)
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
double qDateTimeToDecimalYear(const QDateTime &dateTime)
QList< QgsExpressionFunction * > ExpressionFunctionList
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColorObject(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:96
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:64
const QgsMapLayer * layer
Definition qgsogcutils.h:74
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:75
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30