QGIS API Documentation 4.3.0-Master (16eddb2670e)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
6 email : matthias@opengis.ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
45#include "qgsmagneticmodel.h"
47#include "qgsmessagelog.h"
48#include "qgsmultilinestring.h"
49#include "qgsmultipoint.h"
50#include "qgsogcutils.h"
51#include "qgspolygon.h"
52#include "qgsproviderregistry.h"
53#include "qgsquadrilateral.h"
54#include "qgsrasterbandstats.h"
55#include "qgsrasterlayer.h"
56#include "qgsregularpolygon.h"
57#include "qgsspatialindex.h"
58#include "qgsstringutils.h"
59#include "qgsstyle.h"
60#include "qgssymbollayerutils.h"
61#include "qgsthreadingutils.h"
62#include "qgstransaction.h"
63#include "qgstriangle.h"
64#include "qgsunittypes.h"
65#include "qgsvariantutils.h"
66#include "qgsvectorlayer.h"
68#include "qgsvectorlayerutils.h"
69
70#include <QCryptographicHash>
71#include <QMimeDatabase>
72#include <QProcessEnvironment>
73#include <QRegularExpression>
74#include <QString>
75#include <QUrlQuery>
76#include <QUuid>
77
78using namespace Qt::StringLiterals;
79
82Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
83
84const QString QgsExpressionFunction::helpText() const
85{
86 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
87}
88
90{
91 Q_UNUSED( node )
92 // evaluate arguments
93 QVariantList argValues;
94 if ( args )
95 {
96 int arg = 0;
97 const QList< QgsExpressionNode * > argList = args->list();
98 argValues.resize( argList.size() );
99 QVariant *outArg = argValues.data();
100 for ( QgsExpressionNode *n : argList )
101 {
102 if ( lazyEval() )
103 {
104 // Pass in the node for the function to eval as it needs.
105 *outArg = QVariant::fromValue( n );
106 }
107 else
108 {
109 *outArg = n->eval( parent, context );
111 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
112 if ( QgsExpressionUtils::isNull( *outArg ) && !defaultParamIsNull && !handlesNull() )
113 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
114 }
115 outArg++;
116 arg++;
117 }
118 }
119
120 return func( argValues, context, parent, node );
121}
122
124{
125 Q_UNUSED( node )
126 return true;
127}
128
130{
131 return QStringList();
132}
133
135{
136 Q_UNUSED( parent )
137 Q_UNUSED( context )
138 Q_UNUSED( node )
139 return false;
140}
141
143{
144 Q_UNUSED( parent )
145 Q_UNUSED( context )
146 Q_UNUSED( node )
147 return true;
148}
149
151{
152 Q_UNUSED( node )
153 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
154}
155
157{
158 return mGroups.isEmpty() ? false : mGroups.contains( u"deprecated"_s );
159}
160
162{
163 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
164}
165
167{
168 return mHandlesNull;
169}
170
171// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
174 const QString &fnname,
176 FcnEval fcn,
177 const QString &group,
178 const QString &helpText,
179 const std::function< bool( const QgsExpressionNodeFunction *node ) > &usesGeometry,
180 const std::function< QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
181 bool lazyEval,
182 const QStringList &aliases,
183 bool handlesNull
184)
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{}
193
195{
196 return mAliases;
197}
198
200{
201 if ( mUsesGeometryFunc )
202 return mUsesGeometryFunc( node );
203 else
204 return mUsesGeometry;
205}
206
208{
209 mUsesGeometryFunc = usesGeometry;
210}
211
213{
214 if ( mReferencedColumnsFunc )
215 return mReferencedColumnsFunc( node );
216 else
217 return mReferencedColumns;
218}
219
221{
222 if ( mIsStaticFunc )
223 return mIsStaticFunc( node, parent, context );
224 else
225 return mIsStatic;
226}
227
229{
230 if ( mPrepareFunc )
231 return mPrepareFunc( node, parent, context );
232
233 return true;
234}
235
237{
238 mIsStaticFunc = isStatic;
239}
240
242{
243 mIsStaticFunc = nullptr;
244 mIsStatic = isStatic;
245}
246
247void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
248{
249 mPrepareFunc = prepareFunc;
250}
251
253{
254 if ( node && node->args() )
255 {
256 const QList< QgsExpressionNode * > argList = node->args()->list();
257 for ( QgsExpressionNode *argNode : argList )
258 {
259 if ( !argNode->isStatic( parent, context ) )
260 return false;
261 }
262 }
263
264 return true;
265}
266
267static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
268{
269 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
270 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
271 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
272
273 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
274 return QVariant();
275
276 QVariantList array;
277 int length = 1;
278
279 array << start;
280 double current = start + step;
281 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
282 {
283 array << current;
284 current += step;
285 length++;
286 }
287
288 return array;
289}
290
291static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
292{
293 if ( !context )
294 return QVariant();
295
296 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
297 if ( context->hasGeometry() )
298 return context->geometry();
299 else
300 {
301 FEAT_FROM_CONTEXT( context, f )
302 QgsGeometry geom = f.geometry();
303 if ( !geom.isNull() )
304 return QVariant::fromValue( geom );
305 else
306 return QVariant();
307 }
308}
309
310static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
311{
312 if ( !context )
313 return QVariant();
314
315 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
316
317 if ( name == "feature"_L1 )
318 {
319 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
320 }
321 else if ( name == "id"_L1 )
322 {
323 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
324 }
325 else if ( name == "geometry"_L1 )
326 {
327 return fcnGeometry( values, context, parent, node );
328 }
329 else
330 {
331 return context->variable( name );
332 }
333}
334
335static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
336{
337 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
338 return QgsExpression::replaceExpressionText( templateString, context );
339}
340
341static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
342{
343 if ( !context )
344 return QVariant();
345
346 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
347 QgsExpression expression( expString );
348 return expression.evaluate( context );
349}
350
351static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
352{
353 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
354 return QVariant( std::sqrt( x ) );
355}
356
357static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
358{
359 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
360 return QVariant( std::fabs( val ) );
361}
362
363static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
364{
365 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
366 return ( deg * M_PI ) / 180;
367}
368static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
369{
370 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
371 return ( 180 * rad ) / M_PI;
372}
373static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
374{
375 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
376 return QVariant( std::sin( x ) );
377}
378static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
379{
380 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
381 return QVariant( std::cos( x ) );
382}
383static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
384{
385 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
386 return QVariant( std::tan( x ) );
387}
388static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
389{
390 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
391 return QVariant( std::asin( x ) );
392}
393static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
394{
395 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
396 return QVariant( std::acos( x ) );
397}
398static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
399{
400 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
401 return QVariant( std::atan( x ) );
402}
403static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
404{
405 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
407 return QVariant( std::atan2( y, x ) );
408}
409static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
410{
411 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
412 return QVariant( std::exp( x ) );
413}
414static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
415{
416 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
417 if ( x <= 0 )
418 return QVariant();
419 return QVariant( std::log( x ) );
420}
421static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
422{
423 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
424 if ( x <= 0 )
425 return QVariant();
426 return QVariant( log10( x ) );
427}
428static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
429{
430 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
431 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
432 if ( x <= 0 || b <= 0 )
433 return QVariant();
434 return QVariant( std::log( x ) / std::log( b ) );
435}
436static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
437{
438 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
439 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
440 if ( max < min )
441 return QVariant();
442
443 std::random_device rd;
444 std::mt19937_64 generator( rd() );
445
446 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
447 {
448 quint32 seed;
449 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
450 {
451 // if seed can be converted to int, we use as is
452 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
453 }
454 else
455 {
456 // if not, we hash string representation to int
457 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
458 std::hash<std::string> hasher;
459 seed = hasher( seedStr.toStdString() );
460 }
461 generator.seed( seed );
462 }
463
464 // Return a random double in the range [min, max] (inclusive)
465 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
466 return QVariant( min + f * ( max - min ) );
467}
468static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
469{
470 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
471 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
472 if ( max < min )
473 return QVariant();
474
475 std::random_device rd;
476 std::mt19937_64 generator( rd() );
477
478 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
479 {
480 quint32 seed;
481 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
482 {
483 // if seed can be converted to int, we use as is
484 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
485 }
486 else
487 {
488 // if not, we hash string representation to int
489 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
490 std::hash<std::string> hasher;
491 seed = hasher( seedStr.toStdString() );
492 }
493 generator.seed( seed );
494 }
495
496 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
497 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
498 return QVariant( randomInteger );
499
500 // Prevent wrong conversion of QVariant. See #36412
501 return QVariant( int( randomInteger ) );
502}
503
504static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
505{
506 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
507 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
508 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
509 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
510 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
511
512 if ( domainMin >= domainMax )
513 {
514 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
515 return QVariant();
516 }
517
518 // outside of domain?
519 if ( val >= domainMax )
520 {
521 return rangeMax;
522 }
523 else if ( val <= domainMin )
524 {
525 return rangeMin;
526 }
527
528 // calculate linear scale
529 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
530 double c = rangeMin - ( domainMin * m );
531
532 // Return linearly scaled value
533 return QVariant( m * val + c );
534}
535
536static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
537{
538 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
539 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
540 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
541 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
542 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
543 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
544
545 if ( domainMin >= domainMax )
546 {
547 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
548 return QVariant();
549 }
550 if ( exponent <= 0 )
551 {
552 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
553 return QVariant();
554 }
555
556 // outside of domain?
557 if ( val >= domainMax )
558 {
559 return rangeMax;
560 }
561 else if ( val <= domainMin )
562 {
563 return rangeMin;
564 }
565
566 // Return polynomially scaled value
567 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
568}
569
570static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
571{
572 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
573 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
574 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
575 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
576 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
577 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
578
579 if ( domainMin >= domainMax )
580 {
581 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
582 return QVariant();
583 }
584 if ( exponent <= 0 )
585 {
586 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
587 return QVariant();
588 }
589
590 // outside of domain?
591 if ( val >= domainMax )
592 {
593 return rangeMax;
594 }
595 else if ( val <= domainMin )
596 {
597 return rangeMin;
598 }
599
600 // Return exponentially scaled value
601 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
602 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
603}
604
605static QVariant fcnCubicBezierScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
606{
607 const double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
608 const double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
609 const double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
610 const double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
611 const double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
612
613 const double x1 = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
614 const double y1 = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
615 const double x2 = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
616 const double y2 = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
617
618 if ( x1 < 0.0 || x1 > 1.0 || y1 < 0.0 || y1 > 1.0 || x2 < 0.0 || x2 > 1.0 || y2 < 0.0 || y2 > 1.0 )
619 {
620 parent->setEvalErrorString( QObject::tr( "Cubic bezier control points must be between 0 and 1" ) );
621 return QVariant();
622 }
623
624 if ( domainMin >= domainMax )
625 {
626 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
627 return QVariant();
628 }
629
630 // outside of domain?
631 if ( val >= domainMax )
632 {
633 return rangeMax;
634 }
635 else if ( val <= domainMin )
636 {
637 return rangeMin;
638 }
639
640 // normalize input to [0, 1] range
641 const double t = ( val - domainMin ) / ( domainMax - domainMin );
642
643 // solve using UnitBezier approach (based on MapLibre native's implementation)
644 const double cx = 3.0 * x1;
645 const double bx = 3.0 * ( x2 - x1 ) - cx;
646 const double ax = 1.0 - cx - bx;
647 const double cy = 3.0 * y1;
648 const double by = 3.0 * ( y2 - y1 ) - cy;
649 const double ay = 1.0 - cy - by;
650
651 constexpr double epsilon = 1e-6;
652
653 // solve for s using Newton's method (8 iterations)
654 double s = t;
655 bool solved = false;
656 for ( int i = 0; i < 8; ++i )
657 {
658 const double x2val = ( ( ax * s + bx ) * s + cx ) * s - t;
659 if ( std::fabs( x2val ) < epsilon )
660 {
661 solved = true;
662 break;
663 }
664 const double d2 = ( 3.0 * ax * s + 2.0 * bx ) * s + cx;
665 if ( std::fabs( d2 ) < 1e-6 )
666 break;
667 s = s - x2val / d2;
668 }
669
670 if ( !solved )
671 {
672 // fallback to bisection approach
673 double t0 = 0.0;
674 double t1 = 1.0;
675 s = t;
676
677 if ( s < t0 )
678 {
679 s = t0;
680 solved = true;
681 }
682 else if ( s > t1 )
683 {
684 s = t1;
685 solved = true;
686 }
687
688 while ( !solved && t0 < t1 )
689 {
690 const double x2val = ( ( ax * s + bx ) * s + cx ) * s;
691 if ( std::fabs( x2val - t ) < epsilon )
692 {
693 solved = true;
694 break;
695 }
696 if ( t > x2val )
697 t0 = s;
698 else
699 t1 = s;
700 s = ( t1 - t0 ) * 0.5 + t0;
701 }
702 }
703
704 const double easedT = ( ( ay * s + by ) * s + cy ) * s;
705 return QVariant( ( rangeMax - rangeMin ) * easedT + rangeMin );
706}
707
708static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
709{
710 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
711 double maxVal = std::numeric_limits<double>::quiet_NaN();
712 for ( const QVariant &val : values )
713 {
714 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
715 if ( std::isnan( maxVal ) )
716 {
717 maxVal = testVal;
718 }
719 else if ( !std::isnan( testVal ) )
720 {
721 maxVal = std::max( maxVal, testVal );
722 }
723 }
724
725 if ( !std::isnan( maxVal ) )
726 {
727 result = QVariant( maxVal );
728 }
729 return result;
730}
731
732static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
733{
734 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
735 double minVal = std::numeric_limits<double>::quiet_NaN();
736 for ( const QVariant &val : values )
737 {
738 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
739 if ( std::isnan( minVal ) )
740 {
741 minVal = testVal;
742 }
743 else if ( !std::isnan( testVal ) )
744 {
745 minVal = std::min( minVal, testVal );
746 }
747 }
748
749 if ( !std::isnan( minVal ) )
750 {
751 result = QVariant( minVal );
752 }
753 return result;
754}
755
756static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
757{
758 //lazy eval, so we need to evaluate nodes now
759
760 //first node is layer id or name
761 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
763 QVariant value = node->eval( parent, context );
765
766 // TODO this expression function is NOT thread safe
768 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
770 if ( !vl )
771 {
772 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
773 return QVariant();
774 }
775
776 // second node is aggregate type
777 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
779 value = node->eval( parent, context );
781 bool ok = false;
782 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
783 if ( !ok )
784 {
785 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
786 return QVariant();
787 }
788
789 // third node is subexpression (or field name)
790 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
792 QString subExpression = node->dump();
793
795 //optional forth node is filter
796 if ( values.count() > 3 )
797 {
798 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
800 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
801 if ( !nl || nl->value().isValid() )
802 parameters.filter = node->dump();
803 }
804
805 //optional fifth node is concatenator
806 if ( values.count() > 4 )
807 {
808 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
810 value = node->eval( parent, context );
812 parameters.delimiter = value.toString();
813 }
814
815 //optional sixth node is order by
816 QString orderBy;
817 if ( values.count() > 5 )
818 {
819 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
821 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
822 if ( !nl || nl->value().isValid() )
823 {
824 orderBy = node->dump();
825 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
826 }
827 }
828
829 QString aggregateError;
830 QVariant result;
831 if ( context )
832 {
833 QString cacheKey;
834 QgsExpression subExp( subExpression );
835 QgsExpression filterExp( parameters.filter );
836
837 const QSet< QString > filterVars = filterExp.referencedVariables();
838 const QSet< QString > subExpVars = subExp.referencedVariables();
839 QSet<QString> allVars = filterVars + subExpVars;
840
841 bool isStatic = true;
842 if ( filterVars.contains( u"parent"_s ) || filterVars.contains( QString() ) || subExpVars.contains( u"parent"_s ) || subExpVars.contains( QString() ) )
843 {
844 isStatic = false;
845 }
846 else
847 {
848 for ( const QString &varName : allVars )
849 {
850 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
851 if ( scope && !scope->isStatic( varName ) )
852 {
853 isStatic = false;
854 break;
855 }
856 }
857 }
858
859 if ( isStatic && !parameters.orderBy.isEmpty() )
860 {
861 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
862 {
863 const QgsExpression &orderByExpression { orderByClause.expression() };
864 if ( orderByExpression.referencedVariables().contains( u"parent"_s ) || orderByExpression.referencedVariables().contains( QString() ) )
865 {
866 isStatic = false;
867 break;
868 }
869 }
870 }
871
872 if ( !isStatic )
873 {
874 bool ok = false;
875 const QString contextHash = context->uniqueHash( ok, allVars );
876 if ( ok )
877 {
878 cacheKey = u"aggfcn:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
879 }
880 }
881 else
882 {
883 cacheKey = u"aggfcn:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
884 }
885
886 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
887 {
888 return context->cachedValue( cacheKey );
889 }
890
891 QgsExpressionContext subContext( *context );
893 subScope->setVariable( u"parent"_s, context->feature(), true );
894 subContext.appendScope( subScope );
895 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
896
897 if ( ok && !cacheKey.isEmpty() )
898 {
899 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
900 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
901 // associated with it's calculation!
902 context->setCachedValue( cacheKey, result );
903 }
904 }
905 else
906 {
907 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
908 }
909 if ( !ok )
910 {
911 if ( !aggregateError.isEmpty() )
912 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
913 else
914 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
915 return QVariant();
916 }
917
918 return result;
919}
920
921static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
922{
923 if ( !context )
924 {
925 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
926 return QVariant();
927 }
928
929 // first step - find current layer
930
931 // TODO this expression function is NOT thread safe
933 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
935 if ( !vl )
936 {
937 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
938 return QVariant();
939 }
940
941 //lazy eval, so we need to evaluate nodes now
942
943 //first node is relation name
944 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
946 QVariant value = node->eval( parent, context );
948 QString relationId = value.toString();
949
950 // get project from layer
951 QgsProject *project = vl->project();
952 if ( !project )
953 {
954 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
955 return QVariant();
956 }
957
958 // check relation exists
959 QgsRelation relation = project->relationManager()->relation( relationId );
960
961 if ( !relation.isValid() || relation.referencedLayer() != vl )
962 {
963 // check for relations by name
964 QList< QgsRelation > relations = project->relationManager()->relationsByName( relationId );
965 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
966 {
967 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
968 return QVariant();
969 }
970 else
971 {
972 relation = relations.at( 0 );
973 }
974 }
975
976 QgsVectorLayer *childLayer = relation.referencingLayer();
977
978 // second node is aggregate type
979 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
981 value = node->eval( parent, context );
983 bool ok = false;
984 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
985 if ( !ok )
986 {
987 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
988 return QVariant();
989 }
990
991 //third node is subexpression (or field name)
992 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
994 QString subExpression = node->dump();
995
996 //optional fourth node is concatenator
998 if ( values.count() > 3 )
999 {
1000 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1002 value = node->eval( parent, context );
1004 parameters.delimiter = value.toString();
1005 }
1006
1007 //optional fifth node is order by
1008 QString orderBy;
1009 if ( values.count() > 4 )
1010 {
1011 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
1013 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1014 if ( !nl || nl->value().isValid() )
1015 {
1016 orderBy = node->dump();
1017 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1018 }
1019 }
1020
1021 if ( !context->hasFeature() )
1022 return QVariant();
1023 QgsFeature f = context->feature();
1024
1025 parameters.filter = relation.getRelatedFeaturesFilter( f );
1026
1027 const QString cacheKey = u"relagg:%1%:%2:%3:%4:%5:%6"_s.arg( relationId, vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1028 if ( context->hasCachedValue( cacheKey ) )
1029 return context->cachedValue( cacheKey );
1030
1031 QVariant result;
1032 ok = false;
1033
1034
1035 QgsExpressionContext subContext( *context );
1036 QString error;
1037 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1038
1039 if ( !ok )
1040 {
1041 if ( !error.isEmpty() )
1042 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1043 else
1044 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1045 return QVariant();
1046 }
1047
1048 // cache value
1049 context->setCachedValue( cacheKey, result );
1050 return result;
1051}
1052
1053
1054static QVariant fcnAggregateGeneric(
1055 Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1
1056)
1057{
1058 if ( !context )
1059 {
1060 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1061 return QVariant();
1062 }
1063
1064 // first step - find current layer
1065
1066 // TODO this expression function is NOT thread safe
1068 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
1070 if ( !vl )
1071 {
1072 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1073 return QVariant();
1074 }
1075
1076 //lazy eval, so we need to evaluate nodes now
1077
1078 //first node is subexpression (or field name)
1079 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1081 QString subExpression = node->dump();
1082
1083 //optional second node is group by
1084 QString groupBy;
1085 if ( values.count() > 1 )
1086 {
1087 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
1089 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1090 if ( !nl || nl->value().isValid() )
1091 groupBy = node->dump();
1092 }
1093
1094 //optional third node is filter
1095 if ( values.count() > 2 )
1096 {
1097 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
1099 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1100 if ( !nl || nl->value().isValid() )
1101 parameters.filter = node->dump();
1102 }
1103
1104 //optional order by node, if supported
1105 QString orderBy;
1106 if ( orderByPos >= 0 && values.count() > orderByPos )
1107 {
1108 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
1110 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1111 if ( !nl || nl->value().isValid() )
1112 {
1113 orderBy = node->dump();
1114 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1115 }
1116 }
1117
1118 // build up filter with group by
1119
1120 // find current group by value
1121 if ( !groupBy.isEmpty() )
1122 {
1123 QgsExpression groupByExp( groupBy );
1124 QVariant groupByValue = groupByExp.evaluate( context );
1125 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy, QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s, QgsExpression::quotedValue( groupByValue ) );
1126 if ( !parameters.filter.isEmpty() )
1127 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1128 else
1129 parameters.filter = groupByClause;
1130 }
1131
1132 QgsExpression subExp( subExpression );
1133 QgsExpression filterExp( parameters.filter );
1134
1135 bool isStatic = true;
1136 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1137 for ( const QString &varName : refVars )
1138 {
1139 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1140 if ( scope && !scope->isStatic( varName ) )
1141 {
1142 isStatic = false;
1143 break;
1144 }
1145 }
1146
1147 QString cacheKey;
1148 if ( !isStatic )
1149 {
1150 bool ok = false;
1151 const QString contextHash = context->uniqueHash( ok, refVars );
1152 if ( ok )
1153 {
1154 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
1155 }
1156 }
1157 else
1158 {
1159 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1160 }
1161
1162 if ( context->hasCachedValue( cacheKey ) )
1163 return context->cachedValue( cacheKey );
1164
1165 QVariant result;
1166 bool ok = false;
1167
1168 QgsExpressionContext subContext( *context );
1170 subScope->setVariable( u"parent"_s, context->feature(), true );
1171 subContext.appendScope( subScope );
1172 QString error;
1173 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1174
1175 if ( !ok )
1176 {
1177 if ( !error.isEmpty() )
1178 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1179 else
1180 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1181 return QVariant();
1182 }
1183
1184 // cache value
1185 context->setCachedValue( cacheKey, result );
1186 return result;
1187}
1188
1189
1190static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1191{
1192 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1193}
1194
1195static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1196{
1197 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1198}
1199
1200static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1201{
1202 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1203}
1204
1205static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1206{
1207 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1208}
1209
1210static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1211{
1212 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1213}
1214
1215static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1216{
1217 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1218}
1219
1220static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1221{
1222 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1223}
1224
1225static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1226{
1227 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1228}
1229
1230static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1231{
1232 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1233}
1234
1235static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1236{
1237 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1238}
1239
1240static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1241{
1242 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1243}
1244
1245static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1246{
1247 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1248}
1249
1250static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1251{
1252 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1253}
1254
1255static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1256{
1257 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1258}
1259
1260static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1261{
1262 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1263}
1264
1265static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1266{
1267 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1268}
1269
1270static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1271{
1272 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1273}
1274
1275static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1276{
1277 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1278}
1279
1280static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1281{
1283
1284 //fourth node is concatenator
1285 if ( values.count() > 3 )
1286 {
1287 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1289 QVariant value = node->eval( parent, context );
1291 parameters.delimiter = value.toString();
1292 }
1293
1294 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1295}
1296
1297static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1298{
1300
1301 //fourth node is concatenator
1302 if ( values.count() > 3 )
1303 {
1304 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1306 QVariant value = node->eval( parent, context );
1308 parameters.delimiter = value.toString();
1309 }
1310
1311 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1312}
1313
1314static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1315{
1316 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1317}
1318
1319static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1320{
1321 if ( !context )
1322 return QVariant();
1323
1324 QVariant scale = context->variable( u"map_scale"_s );
1325 bool ok = false;
1326 if ( QgsVariantUtils::isNull( scale ) )
1327 return QVariant();
1328
1329 const double v = scale.toDouble( &ok );
1330 if ( ok )
1331 return v;
1332 return QVariant();
1333}
1334
1335static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1336{
1337 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1338 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1339 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1340
1341 // force testValue to sit inside the range specified by the min and max value
1342 if ( testValue <= minValue )
1343 {
1344 return QVariant( minValue );
1345 }
1346 else if ( testValue >= maxValue )
1347 {
1348 return QVariant( maxValue );
1349 }
1350 else
1351 {
1352 return QVariant( testValue );
1353 }
1354}
1355
1356static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1357{
1358 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1359 return QVariant( std::floor( x ) );
1360}
1361
1362static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1363{
1364 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1365 return QVariant( std::ceil( x ) );
1366}
1367
1368static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1369{
1370 const QVariant value = values.at( 0 );
1371 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1372 {
1373 return QVariant( false );
1374 }
1375 else if ( value.userType() == QMetaType::QString )
1376 {
1377 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1378 return QVariant( !value.toString().isEmpty() );
1379 }
1380 else if ( QgsExpressionUtils::isList( value ) )
1381 {
1382 return !value.toList().isEmpty();
1383 }
1384 return QVariant( value.toBool() );
1385}
1386static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1387{
1388 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1389}
1390static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1391{
1392 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1393}
1394static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1395{
1396 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1397}
1398
1399static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1400{
1401 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1402 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1403 if ( format.isEmpty() && !language.isEmpty() )
1404 {
1405 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1406 return QVariant( QDateTime() );
1407 }
1408
1409 if ( format.isEmpty() && language.isEmpty() )
1410 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1411
1412 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1413 QLocale locale = QLocale();
1414 if ( !language.isEmpty() )
1415 {
1416 locale = QLocale( language );
1417 }
1418
1419 QDateTime datetime = locale.toDateTime( datetimestring, format );
1420 if ( !datetime.isValid() )
1421 {
1422 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1423 datetime = QDateTime();
1424 }
1425 return QVariant( datetime );
1426}
1427
1428static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1429{
1430 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1431 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1432 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1433
1434 const QDate date( year, month, day );
1435 if ( !date.isValid() )
1436 {
1437 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1438 return QVariant();
1439 }
1440 return QVariant( date );
1441}
1442
1443static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1444{
1445 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1446 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1447 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1448
1449 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1450 if ( !time.isValid() )
1451 {
1452 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1453 return QVariant();
1454 }
1455 return QVariant( time );
1456}
1457
1458static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1459{
1460 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1461 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1462 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1463 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1464 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1465 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1466
1467 const QDate date( year, month, day );
1468 if ( !date.isValid() )
1469 {
1470 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1471 return QVariant();
1472 }
1473 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1474 if ( !time.isValid() )
1475 {
1476 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1477 return QVariant();
1478 }
1479 return QVariant( QDateTime( date, time ) );
1480}
1481
1482static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1483{
1484 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1485
1486 QTimeZone tz;
1487
1488#if QT_FEATURE_timezone > 0
1489 if ( !timeZoneId.isEmpty() )
1490 {
1491 tz = QTimeZone( timeZoneId.toUtf8() );
1492 }
1493
1494 if ( !tz.isValid() )
1495 {
1496 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1497 return QVariant();
1498 }
1499
1500#else
1501 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1502#endif
1503 return QVariant::fromValue( tz );
1504}
1505
1506static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1507{
1508#if QT_FEATURE_timezone > 0
1509 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1510 if ( datetime.isValid() )
1511 {
1512 return QVariant::fromValue( datetime.timeZone() );
1513 }
1514 return QVariant();
1515#else
1516 Q_UNUSED( values )
1517 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1518 return QVariant();
1519#endif
1520}
1521
1522static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1523{
1524#if QT_FEATURE_timezone > 0
1525 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1526 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1527 if ( datetime.isValid() && tz.isValid() )
1528 {
1529 datetime.setTimeZone( tz );
1530 return QVariant::fromValue( datetime );
1531 }
1532 return QVariant();
1533#else
1534 Q_UNUSED( values )
1535 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1536 return QVariant();
1537#endif
1538}
1539
1540static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1541{
1542#if QT_FEATURE_timezone > 0
1543 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1544 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1545 if ( datetime.isValid() && tz.isValid() )
1546 {
1547 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1548 }
1549 return QVariant();
1550#else
1551 Q_UNUSED( values )
1552 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1553 return QVariant();
1554#endif
1555}
1556
1557static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1558{
1559#if QT_FEATURE_timezone > 0
1560 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1561 if ( timeZone.isValid() )
1562 {
1563 return QString( timeZone.id() );
1564 }
1565 return QVariant();
1566#else
1567 Q_UNUSED( values )
1568 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1569 return QVariant();
1570#endif
1571}
1572
1573static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1574{
1575 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1576 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1577 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1578 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1579 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1580 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1581 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1582
1583 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1584}
1585
1586static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1587{
1588 for ( const QVariant &value : values )
1589 {
1590 if ( QgsVariantUtils::isNull( value ) )
1591 continue;
1592 return value;
1593 }
1594 return QVariant();
1595}
1596
1597static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1598{
1599 const QVariant val1 = values.at( 0 );
1600 const QVariant val2 = values.at( 1 );
1601
1602 if ( val1 == val2 )
1603 return QVariant();
1604 else
1605 return val1;
1606}
1607
1608static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1609{
1610 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1611 return QVariant( str.toLower() );
1612}
1613
1614static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1615{
1616 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1617 return QVariant( str.toUpper() );
1618}
1619
1620static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1621{
1622 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1623 QStringList elems = str.split( ' ' );
1624 for ( int i = 0; i < elems.size(); i++ )
1625 {
1626 if ( elems[i].size() > 1 )
1627 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1628 }
1629 return QVariant( elems.join( ' '_L1 ) );
1630}
1631
1632static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1633{
1634 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1635 return QVariant( str.trimmed() );
1636}
1637
1638static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1639{
1640 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1641
1642 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1643
1644 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1645 str.replace( re, QString() );
1646 return QVariant( str );
1647}
1648
1649static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1650{
1651 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1652
1653 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1654
1655 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1656 str.replace( re, QString() );
1657 return QVariant( str );
1658}
1659
1660static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1661{
1662 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1663 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1664 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1665}
1666
1667static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1668{
1669 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1670 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1671 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1672}
1673
1674static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1675{
1676 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1677 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1678 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1679 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1680}
1681
1682static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1683{
1684 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1685 return QVariant( QgsStringUtils::soundex( string ) );
1686}
1687
1688static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689{
1690 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1691 return QVariant( QString( character ) );
1692}
1693
1694static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1695{
1696 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1697
1698 if ( value.isEmpty() )
1699 {
1700 return QVariant();
1701 }
1702
1703 int res = value.at( 0 ).unicode();
1704 return QVariant( res );
1705}
1706
1707static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1708{
1709 if ( values.length() == 2 || values.length() == 3 )
1710 {
1711 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1712 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1713
1714 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1715
1716 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1717 }
1718
1719 return QVariant();
1720}
1721
1722static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1723{
1724 // two variants, one for geometry, one for string
1725
1726 //geometry variant
1727 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1728 if ( !geom.isNull() )
1729 {
1730 if ( geom.type() == Qgis::GeometryType::Line )
1731 return QVariant( geom.length() );
1732 else
1733 return QVariant();
1734 }
1735
1736 //otherwise fall back to string variant
1737 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1738 return QVariant( str.length() );
1739}
1740
1741static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1742{
1743 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1744
1745 if ( geom.type() != Qgis::GeometryType::Line )
1746 return QVariant();
1747
1748 double totalLength = 0;
1749 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1750 {
1752 {
1753 totalLength += line->length3D();
1754 }
1755 else
1756 {
1757 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1758 totalLength += segmentized->length3D();
1759 }
1760 }
1761
1762 return totalLength;
1763}
1764
1765
1766static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1767{
1768 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1769 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1770 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1771}
1772
1773static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1774{
1775 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1776 {
1777 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1778 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1779 QVector< QPair< QString, QString > > mapItems;
1780
1781 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1782 {
1783 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1784 }
1785
1786 // larger keys should be replaced first since they may contain whole smaller keys
1787 std::sort( mapItems.begin(), mapItems.end(), []( const QPair< QString, QString > &pair1, const QPair< QString, QString > &pair2 ) { return ( pair1.first.length() > pair2.first.length() ); } );
1788
1789 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1790 {
1791 str = str.replace( it->first, it->second );
1792 }
1793
1794 return QVariant( str );
1795 }
1796 else if ( values.count() == 3 )
1797 {
1798 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1799 QVariantList before;
1800 QVariantList after;
1801 bool isSingleReplacement = false;
1802
1803 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1804 {
1805 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1806 }
1807 else
1808 {
1809 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1810 }
1811
1812 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1813 {
1814 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1815 isSingleReplacement = true;
1816 }
1817 else
1818 {
1819 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1820 }
1821
1822 if ( !isSingleReplacement && before.length() != after.length() )
1823 {
1824 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1825 return QVariant();
1826 }
1827
1828 for ( int i = 0; i < before.length(); i++ )
1829 {
1830 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1831 }
1832
1833 return QVariant( str );
1834 }
1835 else
1836 {
1837 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1838 return QVariant();
1839 }
1840}
1841
1842static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1843{
1844 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1845 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1846 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1847
1848 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1849 if ( !re.isValid() )
1850 {
1851 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1852 return QVariant();
1853 }
1854 return QVariant( str.replace( re, after ) );
1855}
1856
1857static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1858{
1859 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1860 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1861
1862 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1863 if ( !re.isValid() )
1864 {
1865 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1866 return QVariant();
1867 }
1868 return QVariant( ( str.indexOf( re ) + 1 ) );
1869}
1870
1871static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1872{
1873 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1874 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1875 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1876
1877 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1878 if ( !re.isValid() )
1879 {
1880 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1881 return QVariant();
1882 }
1883
1884 QRegularExpressionMatch matches = re.match( str );
1885 if ( matches.hasMatch() )
1886 {
1887 QVariantList array;
1888 QStringList list = matches.capturedTexts();
1889
1890 // Skip the first string to only return captured groups
1891 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1892 {
1893 array += ( !( *it ).isEmpty() ) ? *it : empty;
1894 }
1895
1896 return QVariant( array );
1897 }
1898 else
1899 {
1900 return QVariant();
1901 }
1902}
1903
1904static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1905{
1906 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1907 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1908
1909 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1910 if ( !re.isValid() )
1911 {
1912 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1913 return QVariant();
1914 }
1915
1916 // extract substring
1917 QRegularExpressionMatch match = re.match( str );
1918 if ( match.hasMatch() )
1919 {
1920 // return first capture
1921 if ( match.lastCapturedIndex() > 0 )
1922 {
1923 // a capture group was present, so use that
1924 return QVariant( match.captured( 1 ) );
1925 }
1926 else
1927 {
1928 // no capture group, so using all match
1929 return QVariant( match.captured( 0 ) );
1930 }
1931 }
1932 else
1933 {
1934 return QVariant( "" );
1935 }
1936}
1937
1938static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1939{
1940 const int version = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1941 QUuid uuid;
1942 switch ( version )
1943 {
1944 case 4:
1945 uuid = QUuid::createUuid();
1946 break;
1947 case 7:
1948#if QT_VERSION >= QT_VERSION_CHECK( 6, 9, 0 )
1949 uuid = QUuid::createUuidV7();
1950#else
1951 parent->setEvalErrorString( QObject::tr( "UUid version 7 is not supported on this QGIS build" ) );
1952 return QVariant();
1953#endif
1954 break;
1955 default:
1956 parent->setEvalErrorString( QObject::tr( "UUid version %1 is not supported" ).arg( version ) );
1957 return QVariant();
1958 }
1959
1960 const QString format = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1961 if ( format.compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1962 return uuid.toString( QUuid::StringFormat::WithoutBraces );
1963 else if ( format.compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1964 return uuid.toString( QUuid::StringFormat::Id128 );
1965 return uuid.toString();
1966}
1967
1968static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1969{
1970 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1971 return QVariant();
1972
1973 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1974 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1975
1976 int len = 0;
1977 if ( values.at( 2 ).isValid() )
1978 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1979 else
1980 len = str.size();
1981
1982 if ( from < 0 )
1983 {
1984 from = str.size() + from;
1985 if ( from < 0 )
1986 {
1987 from = 0;
1988 }
1989 }
1990 else if ( from > 0 )
1991 {
1992 //account for the fact that substr() starts at 1
1993 from -= 1;
1994 }
1995
1996 if ( len < 0 )
1997 {
1998 len = str.size() + len - from;
1999 if ( len < 0 )
2000 {
2001 len = 0;
2002 }
2003 }
2004
2005 return QVariant( str.mid( from, len ) );
2006}
2007static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2008{
2009 FEAT_FROM_CONTEXT( context, f )
2010 return QVariant( f.id() );
2011}
2012
2013static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2014{
2015 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2016 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
2017 bool foundLayer = false;
2018 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2019 values.at( 0 ),
2020 context,
2021 parent,
2022 [parent, bandNb, geom]( QgsMapLayer *mapLayer ) {
2023 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
2024 if ( !layer || !layer->dataProvider() )
2025 {
2026 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2027 return QVariant();
2028 }
2029
2030 if ( bandNb < 1 || bandNb > layer->bandCount() )
2031 {
2032 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
2033 return QVariant();
2034 }
2035
2036 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
2037 {
2038 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
2039 return QVariant();
2040 }
2041
2042 QgsPointXY point = geom.asPoint();
2043 if ( geom.isMultipart() )
2044 {
2045 QgsMultiPointXY multiPoint = geom.asMultiPoint();
2046 if ( multiPoint.count() == 1 )
2047 {
2048 point = multiPoint[0];
2049 }
2050 else
2051 {
2052 // if the geometry contains more than one part, return an undefined value
2053 return QVariant();
2054 }
2055 }
2056
2057 double value = layer->dataProvider()->sample( point, bandNb );
2058 return std::isnan( value ) ? QVariant() : value;
2059 },
2060 foundLayer
2061 );
2062
2063 if ( !foundLayer )
2064 {
2065 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2066 return QVariant();
2067 }
2068 else
2069 {
2070 return res;
2071 }
2072}
2073
2074static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2075{
2076 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2077 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2078
2079 bool foundLayer = false;
2080 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2081 values.at( 0 ),
2082 context,
2083 parent,
2084 [parent, bandNb, value]( QgsMapLayer *mapLayer ) -> QVariant {
2085 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
2086 if ( !layer || !layer->dataProvider() )
2087 {
2088 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2089 return QVariant();
2090 }
2091
2092 if ( bandNb < 1 || bandNb > layer->bandCount() )
2093 {
2094 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
2095 return QVariant();
2096 }
2097
2098 if ( std::isnan( value ) )
2099 {
2100 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
2101 return QVariant();
2102 }
2103
2104 if ( !layer->dataProvider()->attributeTable( bandNb ) )
2105 {
2106 return QVariant();
2107 }
2108
2109 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
2110 if ( data.isEmpty() )
2111 {
2112 return QVariant();
2113 }
2114
2115 QVariantMap result;
2116 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
2117 for ( int idx = 0; idx < static_cast<int>( fields.count() ) && idx < static_cast<int>( data.count() ); ++idx )
2118 {
2119 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
2120 if ( field.isColor() || field.isRamp() )
2121 {
2122 continue;
2123 }
2124 result.insert( fields.at( idx ).name, data.at( idx ) );
2125 }
2126
2127 return result;
2128 },
2129 foundLayer
2130 );
2131
2132 if ( !foundLayer )
2133 {
2134 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2135 return QVariant();
2136 }
2137 else
2138 {
2139 return res;
2140 }
2141}
2142
2143static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2144{
2145 if ( !context )
2146 return QVariant();
2147
2148 return context->feature();
2149}
2150
2151static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2152{
2153 QgsFeature feature;
2154 QString attr;
2155 if ( values.size() == 1 )
2156 {
2157 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2158 feature = context->feature();
2159 }
2160 else if ( values.size() == 2 )
2161 {
2162 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2163 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2164 }
2165 else
2166 {
2167 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2168 return QVariant();
2169 }
2170
2171 return feature.attribute( attr );
2172}
2173
2174static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2175{
2176 QString table { R"html(
2177 <table>
2178 <thead>
2179 <tr><th>%1</th></tr>
2180 </thead>
2181 <tbody>
2182 <tr><td>%2</td></tr>
2183 </tbody>
2184 </table>)html" };
2185 QVariantMap dict;
2186 if ( values.size() == 1 )
2187 {
2188 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2189 }
2190 else
2191 {
2192 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2193 return QVariant();
2194 }
2195
2196 if ( dict.isEmpty() )
2197 {
2198 return QVariant();
2199 }
2200
2201 QStringList headers;
2202 QStringList cells;
2203
2204 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2205 {
2206 headers.push_back( it.key().toHtmlEscaped() );
2207 cells.push_back( it.value().toString().toHtmlEscaped() );
2208 }
2209
2210 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2211}
2212
2213static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2214{
2215 QString table { R"html(
2216 <dl>
2217 %1
2218 </dl>)html" };
2219 QVariantMap dict;
2220 if ( values.size() == 1 )
2221 {
2222 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2223 }
2224 else
2225 {
2226 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2227 return QVariant();
2228 }
2229
2230 if ( dict.isEmpty() )
2231 {
2232 return QVariant();
2233 }
2234
2235 QString rows;
2236
2237 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2238 {
2239 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2240 }
2241
2242 return table.arg( rows );
2243}
2244
2245static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2246{
2247 QVariant layer;
2248 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2249 {
2250 layer = context->variable( u"layer"_s );
2251 }
2252 else
2253 {
2254 //first node is layer id or name
2255 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2257 layer = node->eval( parent, context );
2259 }
2260
2261 QgsFeature feature;
2262 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2263 {
2264 feature = context->feature();
2265 }
2266 else
2267 {
2268 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2269 }
2270
2272 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2273 if ( strength == "hard"_L1 )
2274 {
2276 }
2277 else if ( strength == "soft"_L1 )
2278 {
2280 }
2281
2282 bool foundLayer = false;
2283 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2284 layer,
2285 context,
2286 parent,
2287 [parent, feature, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2288 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2289 if ( !layer )
2290 {
2291 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2292 return QVariant();
2293 }
2294
2295 const QgsFields fields = layer->fields();
2296 bool valid = true;
2297 for ( int i = 0; i < fields.size(); i++ )
2298 {
2299 QStringList errors;
2300 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2301 if ( !valid )
2302 {
2303 break;
2304 }
2305 }
2306
2307 return valid;
2308 },
2309 foundLayer
2310 );
2311
2312 if ( !foundLayer )
2313 {
2314 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2315 return QVariant();
2316 }
2317
2318 return res;
2319}
2320
2321static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2322{
2323 QVariant layer;
2324 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2325 {
2326 layer = context->variable( u"layer"_s );
2327 }
2328 else
2329 {
2330 //first node is layer id or name
2331 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2333 layer = node->eval( parent, context );
2335 }
2336
2337 QgsFeature feature;
2338 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2339 {
2340 feature = context->feature();
2341 }
2342 else
2343 {
2344 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2345 }
2346
2348 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2349 if ( strength == "hard"_L1 )
2350 {
2352 }
2353 else if ( strength == "soft"_L1 )
2354 {
2356 }
2357
2358 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2359
2360 bool foundLayer = false;
2361 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2362 layer,
2363 context,
2364 parent,
2365 [parent, feature, attributeName, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2366 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2367 if ( !layer )
2368 {
2369 return QVariant();
2370 }
2371
2372 const int fieldIndex = layer->fields().indexFromName( attributeName );
2373 if ( fieldIndex == -1 )
2374 {
2375 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2376 return QVariant();
2377 }
2378
2379 QStringList errors;
2380 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2381 return valid;
2382 },
2383 foundLayer
2384 );
2385
2386 if ( !foundLayer )
2387 {
2388 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2389 return QVariant();
2390 }
2391
2392 return res;
2393}
2394
2395static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2396{
2397 QgsFeature feature;
2398 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2399 {
2400 feature = context->feature();
2401 }
2402 else
2403 {
2404 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2405 }
2406
2407 const QgsFields fields = feature.fields();
2408 QVariantMap result;
2409 for ( int i = 0; i < fields.count(); ++i )
2410 {
2411 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2412 }
2413 return result;
2414}
2415
2416static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2417{
2418 QgsVectorLayer *layer = nullptr;
2419 QgsFeature feature;
2420
2421 // TODO this expression function is NOT thread safe
2423 if ( values.isEmpty() )
2424 {
2425 feature = context->feature();
2426 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2427 }
2428 else if ( values.size() == 1 )
2429 {
2430 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2431 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2432 }
2433 else if ( values.size() == 2 )
2434 {
2435 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2436 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2437 }
2438 else
2439 {
2440 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2441 return QVariant();
2442 }
2444
2445 if ( !layer )
2446 {
2447 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2448 return QVariant();
2449 }
2450
2451 if ( !feature.isValid() )
2452 {
2453 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2454 return QVariant();
2455 }
2456
2457 const QgsFields fields = feature.fields();
2458 QVariantMap result;
2459 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2460 {
2461 const QString fieldName { fields.at( fieldIndex ).name() };
2462 const QVariant attributeVal = feature.attribute( fieldIndex );
2463 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2464 if ( context && context->hasCachedValue( cacheValueKey ) )
2465 {
2466 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2467 }
2468 else
2469 {
2470 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2472 QVariant cache;
2473 if ( context )
2474 {
2475 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2476
2477 if ( !context->hasCachedValue( cacheKey ) )
2478 {
2479 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2480 context->setCachedValue( cacheKey, cache );
2481 }
2482 else
2483 {
2484 cache = context->cachedValue( cacheKey );
2485 }
2486 }
2487 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2488
2489 result.insert( fields.at( fieldIndex ).name(), value );
2490
2491 if ( context )
2492 {
2493 context->setCachedValue( cacheValueKey, value );
2494 }
2495 }
2496 }
2497 return result;
2498}
2499
2500static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2501{
2502 QgsVectorLayer *layer = nullptr;
2503 QgsFeature feature;
2504 bool evaluate = true;
2505
2506 // TODO this expression function is NOT thread safe
2508 if ( values.isEmpty() )
2509 {
2510 feature = context->feature();
2511 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2512 }
2513 else if ( values.size() == 1 )
2514 {
2515 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2516 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2517 }
2518 else if ( values.size() == 2 )
2519 {
2520 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2521 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2522 }
2523 else if ( values.size() == 3 )
2524 {
2525 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2526 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2527 evaluate = values.value( 2 ).toBool();
2528 }
2529 else
2530 {
2531 if ( isMaptip )
2532 {
2533 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2534 }
2535 else
2536 {
2537 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2538 }
2539 return QVariant();
2540 }
2541
2542 if ( !layer )
2543 {
2544 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2545 return QVariant();
2546 }
2548
2549 if ( !feature.isValid() )
2550 {
2551 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2552 return QVariant();
2553 }
2554
2555 if ( !evaluate )
2556 {
2557 if ( isMaptip )
2558 {
2559 return layer->mapTipTemplate();
2560 }
2561 else
2562 {
2563 return layer->displayExpression();
2564 }
2565 }
2566
2567 QgsExpressionContext subContext( *context );
2568 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2569 subContext.setFeature( feature );
2570
2571 if ( isMaptip )
2572 {
2573 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2574 }
2575 else
2576 {
2577 QgsExpression exp( layer->displayExpression() );
2578 exp.prepare( &subContext );
2579 return exp.evaluate( &subContext ).toString();
2580 }
2581}
2582
2583static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2584{
2585 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2586}
2587
2588static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2589{
2590 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2591}
2592
2593static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2594{
2595 QgsFeature feature;
2596 QVariant layer;
2597 if ( values.isEmpty() )
2598 {
2599 feature = context->feature();
2600 layer = context->variable( u"layer"_s );
2601 }
2602 else if ( values.size() == 1 )
2603 {
2604 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2605 layer = context->variable( u"layer"_s );
2606 }
2607 else if ( values.size() == 2 )
2608 {
2609 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2610 layer = values.at( 0 );
2611 }
2612 else
2613 {
2614 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2615 return QVariant();
2616 }
2617
2618 bool foundLayer = false;
2619 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2620 layer,
2621 context,
2622 parent,
2623 [feature]( QgsMapLayer *mapLayer ) -> QVariant {
2624 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2625 if ( !layer || !feature.isValid() )
2626 {
2627 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2628 }
2629
2630 return layer->selectedFeatureIds().contains( feature.id() );
2631 },
2632 foundLayer
2633 );
2634 if ( !foundLayer )
2635 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2636 else
2637 return res;
2638}
2639
2640static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2641{
2642 QVariant layer;
2643
2644 if ( values.isEmpty() )
2645 layer = context->variable( u"layer"_s );
2646 else if ( values.count() == 1 )
2647 layer = values.at( 0 );
2648 else
2649 {
2650 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2651 return QVariant();
2652 }
2653
2654 bool foundLayer = false;
2655 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2656 layer,
2657 context,
2658 parent,
2659 []( QgsMapLayer *mapLayer ) -> QVariant {
2660 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2661 if ( !layer )
2662 {
2663 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2664 }
2665
2666 return layer->selectedFeatureCount();
2667 },
2668 foundLayer
2669 );
2670 if ( !foundLayer )
2671 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2672 else
2673 return res;
2674}
2675
2676static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2677{
2678 static QMap<QString, qlonglong> counterCache;
2679 QVariant functionResult;
2680
2681 auto fetchAndIncrementFunc = [values, parent, &functionResult]( QgsMapLayer *mapLayer, const QString &databaseArgument ) {
2682 QString database;
2683
2684 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2685
2686 if ( layer )
2687 {
2688 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2689 database = decodedUri.value( u"path"_s ).toString();
2690 if ( database.isEmpty() )
2691 {
2692 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2693 }
2694 }
2695 else
2696 {
2697 database = databaseArgument;
2698 }
2699
2700 const QString table = values.at( 1 ).toString();
2701 const QString idColumn = values.at( 2 ).toString();
2702 const QString filterAttribute = values.at( 3 ).toString();
2703 const QVariant filterValue = values.at( 4 ).toString();
2704 const QVariantMap defaultValues = values.at( 5 ).toMap();
2705
2706 // read from database
2708 sqlite3_statement_unique_ptr sqliteStatement;
2709
2710 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2711 {
2712 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2713 functionResult = QVariant();
2714 return;
2715 }
2716
2717 QString errorMessage;
2718 QString currentValSql;
2719
2720 qlonglong nextId = 0;
2721 bool cachedMode = false;
2722 bool valueRetrieved = false;
2723
2724 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2725
2726 // Running in transaction mode, check for cached value first
2727 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2728 {
2729 cachedMode = true;
2730
2731 auto cachedCounter = counterCache.find( cacheString );
2732
2733 if ( cachedCounter != counterCache.end() )
2734 {
2735 qlonglong &cachedValue = cachedCounter.value();
2736 nextId = cachedValue;
2737 nextId += 1;
2738 cachedValue = nextId;
2739 valueRetrieved = true;
2740 }
2741 }
2742
2743 // Either not in cached mode or no cached value found, obtain from DB
2744 if ( !cachedMode || !valueRetrieved )
2745 {
2746 int result = SQLITE_ERROR;
2747
2748 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2749 if ( !filterAttribute.isNull() )
2750 {
2751 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2752 }
2753
2754 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2755
2756 if ( result == SQLITE_OK )
2757 {
2758 nextId = 0;
2759 if ( sqliteStatement.step() == SQLITE_ROW )
2760 {
2761 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2762 }
2763
2764 // If in cached mode: add value to cache and connect to transaction
2765 if ( cachedMode && result == SQLITE_OK )
2766 {
2767 counterCache.insert( cacheString, nextId );
2768
2769 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() { counterCache.remove( cacheString ); } );
2770 }
2771 valueRetrieved = true;
2772 }
2773 }
2774
2775 if ( valueRetrieved )
2776 {
2777 QString upsertSql;
2778 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2779 QStringList cols;
2780 QStringList vals;
2781 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2782 vals << QgsSqliteUtils::quotedValue( nextId );
2783
2784 if ( !filterAttribute.isNull() )
2785 {
2786 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2787 vals << QgsSqliteUtils::quotedValue( filterValue );
2788 }
2789
2790 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2791 {
2792 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2793 vals << iter.value().toString();
2794 }
2795
2796 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2797 upsertSql += " VALUES "_L1;
2798 upsertSql += '(' + vals.join( ',' ) + ')';
2799
2800 int result = SQLITE_ERROR;
2801 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2802 {
2803 QgsTransaction *transaction = layer->dataProvider()->transaction();
2804 if ( transaction->executeSql( upsertSql, errorMessage ) )
2805 {
2806 result = SQLITE_OK;
2807 }
2808 }
2809 else
2810 {
2811 result = sqliteDb.exec( upsertSql, errorMessage );
2812 }
2813 if ( result == SQLITE_OK )
2814 {
2815 functionResult = QVariant( nextId );
2816 return;
2817 }
2818 else
2819 {
2820 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2821 functionResult = QVariant();
2822 return;
2823 }
2824 }
2825
2826 functionResult = QVariant();
2827 };
2828
2829 bool foundLayer = false;
2830 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer *layer ) { fetchAndIncrementFunc( layer, QString() ); }, foundLayer );
2831 if ( !foundLayer )
2832 {
2833 const QString databasePath = values.at( 0 ).toString();
2834 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath] { fetchAndIncrementFunc( nullptr, databasePath ); } );
2835 }
2836
2837 return functionResult;
2838}
2839
2840static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2841{
2842 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2843 if ( !crs.isValid() )
2844 return QVariant();
2845 return crs.authid();
2846}
2847
2848static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2849{
2850 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2851 QgsCoordinateReferenceSystem crs( definition );
2852
2853 if ( !crs.isValid() )
2854 {
2855 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2856 }
2857
2858 return QVariant::fromValue( crs );
2859}
2860
2861static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2862{
2863 QString concat( "" );
2864 for ( const QVariant &value : values )
2865 {
2866 if ( !QgsVariantUtils::isNull( value ) )
2867 concat += QgsExpressionUtils::getStringValue( value, parent );
2868 }
2869 return concat;
2870}
2871
2872static QVariant fcnConcatWs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2873{
2874 if ( values.length() < 2 )
2875 {
2876 parent->setEvalErrorString( QObject::tr( "Function concat_ws requires at least 2 arguments" ) );
2877 return QVariant();
2878 }
2879
2880 const QString separator = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2881
2882 QStringList stringValues;
2883 stringValues.reserve( values.size() - 1 );
2884 for ( int i = 1; i < values.size(); ++i )
2885 {
2886 const QVariant value = values.at( i );
2887 if ( !QgsVariantUtils::isNull( value ) )
2888 {
2889 stringValues.append( QgsExpressionUtils::getStringValue( value, parent ) );
2890 }
2891 }
2892
2893 return stringValues.join( separator );
2894}
2895
2896static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2897{
2898 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2899 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2900}
2901
2902static QVariant fcnUnaccent( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction *node )
2903{
2904 Q_UNUSED( context )
2905 Q_UNUSED( node )
2906
2907 if ( values.isEmpty() || values[0].isNull() )
2908 return QVariant();
2909
2910 return QgsStringUtils::unaccent( values[0].toString() );
2911}
2912
2913static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2914{
2915 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2916 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2917 return string.right( pos );
2918}
2919
2920static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2921{
2922 if ( values.length() < 2 || values.length() > 3 )
2923 return QVariant();
2924
2925 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2926 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2927
2928 bool overlapping = false;
2929 if ( values.length() == 3 )
2930 {
2931 overlapping = values.at( 2 ).toBool();
2932 }
2933
2934 if ( substring.isEmpty() )
2935 return QVariant( 0 );
2936
2937 int count = 0;
2938 if ( overlapping )
2939 {
2940 count = input.count( substring );
2941 }
2942 else
2943 {
2944 int pos = 0;
2945 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2946 {
2947 count++;
2948 pos += substring.length();
2949 }
2950 }
2951
2952 return QVariant( count );
2953}
2954
2955static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2956{
2957 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2958 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2959 return string.left( pos );
2960}
2961
2962static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2963{
2964 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2965 const int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2966 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2967 if ( fill.isEmpty() )
2968 {
2969 fill = u" "_s;
2970 }
2971 return string.leftJustified( length, fill.at( 0 ), true );
2972}
2973
2974static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2975{
2976 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2977 const int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2978 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2979 if ( fill.isEmpty() )
2980 {
2981 fill = u" "_s;
2982 }
2983 return string.rightJustified( length, fill.at( 0 ), true );
2984}
2985
2986static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2987{
2988 if ( values.size() < 1 )
2989 {
2990 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2991 return QVariant();
2992 }
2993
2994 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2995 for ( int n = 1; n < values.length(); n++ )
2996 {
2997 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2998 }
2999 return string;
3000}
3001
3002
3003static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3004{
3005 return QVariant( QDateTime::currentDateTime() );
3006}
3007
3008static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3009{
3010 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3011 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3012 if ( format.isEmpty() && !language.isEmpty() )
3013 {
3014 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
3015 return QVariant( QDate() );
3016 }
3017
3018 if ( format.isEmpty() && language.isEmpty() )
3019 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
3020
3021 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3022 QLocale locale = QLocale();
3023 if ( !language.isEmpty() )
3024 {
3025 locale = QLocale( language );
3026 }
3027
3028 QDate date = locale.toDate( datestring, format );
3029 if ( !date.isValid() )
3030 {
3031 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
3032 date = QDate();
3033 }
3034 return QVariant( date );
3035}
3036
3037static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3038{
3039 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3040 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3041 if ( format.isEmpty() && !language.isEmpty() )
3042 {
3043 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
3044 return QVariant( QTime() );
3045 }
3046
3047 if ( format.isEmpty() && language.isEmpty() )
3048 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
3049
3050 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3051 QLocale locale = QLocale();
3052 if ( !language.isEmpty() )
3053 {
3054 locale = QLocale( language );
3055 }
3056
3057 QTime time = locale.toTime( timestring, format );
3058 if ( !time.isValid() )
3059 {
3060 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
3061 time = QTime();
3062 }
3063 return QVariant( time );
3064}
3065
3066static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3067{
3068 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
3069}
3070
3071/*
3072 * DMS functions
3073 */
3074
3075static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3076{
3077 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3078 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3079 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3080
3081 QString formatString;
3082 if ( values.count() > 3 )
3083 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
3084
3086 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
3087 {
3089 }
3090 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
3091 {
3093 }
3094 else if ( !formatString.isEmpty() )
3095 {
3096 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
3097 return QVariant();
3098 }
3099
3100 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
3101 {
3102 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
3103 }
3104 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
3105 {
3106 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
3107 }
3108 else
3109 {
3110 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
3111 return QVariant();
3112 }
3113}
3114
3115static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3116{
3118 return floatToDegreeFormat( format, values, context, parent, node );
3119}
3120
3121static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3122{
3123 double value = 0.0;
3124 bool ok = false;
3125 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
3126
3127 return ok ? QVariant( value ) : QVariant();
3128}
3129
3130static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3131{
3133 return floatToDegreeFormat( format, values, context, parent, node );
3134}
3135
3136static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3137{
3138 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3139 return static_cast< int >( decimalDegrees );
3140}
3141
3142static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3143{
3144 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3145 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3146 return static_cast< int >( remainder * 60 );
3147}
3148
3149static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3150{
3151 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3152 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3153 const double remainderInMinutes = remainder * 60;
3154 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
3155 // do not truncate to int, this function returns decimal seconds!
3156 return remainderSecondsFraction * 60;
3157}
3158
3159static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3160{
3161 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3162 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3163 qint64 seconds = d2.secsTo( d1 );
3164 return QVariant::fromValue( QgsInterval( seconds ) );
3165}
3166
3167static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3168{
3169 if ( !values.at( 0 ).canConvert<QDate>() )
3170 return QVariant();
3171
3172 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
3173 if ( !date.isValid() )
3174 return QVariant();
3175
3176 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
3177 // (to match PostgreSQL behavior)
3178 return date.dayOfWeek() % 7;
3179}
3180
3181static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3182{
3183 QVariant value = values.at( 0 );
3184 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3185 if ( inter.isValid() )
3186 {
3187 return QVariant( inter.days() );
3188 }
3189 else
3190 {
3191 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3192 return QVariant( d1.date().day() );
3193 }
3194}
3195
3196static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3197{
3198 QVariant value = values.at( 0 );
3199 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3200 if ( inter.isValid() )
3201 {
3202 return QVariant( inter.years() );
3203 }
3204 else
3205 {
3206 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3207 return QVariant( d1.date().year() );
3208 }
3209}
3210
3211static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3212{
3213 QVariant value = values.at( 0 );
3214 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3215 if ( inter.isValid() )
3216 {
3217 return QVariant( inter.months() );
3218 }
3219 else
3220 {
3221 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3222 return QVariant( d1.date().month() );
3223 }
3224}
3225
3226static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3227{
3228 QVariant value = values.at( 0 );
3229 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3230 if ( inter.isValid() )
3231 {
3232 return QVariant( inter.weeks() );
3233 }
3234 else
3235 {
3236 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3237 return QVariant( d1.date().weekNumber() );
3238 }
3239}
3240
3241static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3242{
3243 QVariant value = values.at( 0 );
3244 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3245 if ( inter.isValid() )
3246 {
3247 return QVariant( inter.hours() );
3248 }
3249 else
3250 {
3251 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3252 return QVariant( t1.hour() );
3253 }
3254}
3255
3256static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3257{
3258 QVariant value = values.at( 0 );
3259 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3260 if ( inter.isValid() )
3261 {
3262 return QVariant( inter.minutes() );
3263 }
3264 else
3265 {
3266 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3267 return QVariant( t1.minute() );
3268 }
3269}
3270
3271static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3272{
3273 QVariant value = values.at( 0 );
3274 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3275 if ( inter.isValid() )
3276 {
3277 return QVariant( inter.seconds() );
3278 }
3279 else
3280 {
3281 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3282 return QVariant( t1.second() );
3283 }
3284}
3285
3286static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3287{
3288 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3289 if ( dt.isValid() )
3290 {
3291 return QVariant( dt.toMSecsSinceEpoch() );
3292 }
3293 else
3294 {
3295 return QVariant();
3296 }
3297}
3298
3299static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3300{
3301 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3302 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3303 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3304}
3305
3306static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3307{
3308 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3309 if ( parent->hasEvalError() )
3310 {
3311 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3312 return QVariant();
3313 }
3314 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3315 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3316}
3317
3318static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3320 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3321 if ( parent->hasEvalError() )
3322 {
3323 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3324 return QVariant();
3325 }
3326 bool ok;
3327 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3328}
3329
3330double qDateTimeToDecimalYear( const QDateTime &dateTime )
3331{
3332 if ( !dateTime.isValid() )
3333 {
3334 return 0.0;
3335 }
3336
3337 const int year = dateTime.date().year();
3338 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3339 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3340 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3341 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3342 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3343}
3344
3345static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3346{
3347 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3348 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3349 if ( parent->hasEvalError() )
3350 {
3351 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3352 return QVariant();
3353 }
3354 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3355 if ( parent->hasEvalError() )
3356 {
3357 return QVariant();
3358 }
3359 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3360 if ( parent->hasEvalError() )
3361 {
3362 return QVariant();
3363 }
3364 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3365 if ( parent->hasEvalError() )
3366 {
3367 return QVariant();
3368 }
3369 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3370
3371 const QgsMagneticModel model( name, filePath );
3372 try
3373 {
3374 double declination = 0;
3375 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3376 {
3377 return declination;
3378 }
3379 else
3380 {
3381 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3382 }
3383 }
3384 catch ( QgsNotSupportedException &e )
3385 {
3386 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3387 }
3388 return QVariant();
3389}
3390
3391static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3392{
3393 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3394 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3395 if ( parent->hasEvalError() )
3396 {
3397 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3398 return QVariant();
3399 }
3400 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3401 if ( parent->hasEvalError() )
3402 {
3403 return QVariant();
3404 }
3405 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3406 if ( parent->hasEvalError() )
3407 {
3408 return QVariant();
3409 }
3410 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3411 if ( parent->hasEvalError() )
3412 {
3413 return QVariant();
3414 }
3415 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3416
3417 const QgsMagneticModel model( name, filePath );
3418 try
3419 {
3420 double inclination = 0;
3421 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3422 {
3423 return inclination;
3424 }
3425 else
3426 {
3427 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3428 }
3429 }
3430 catch ( QgsNotSupportedException &e )
3431 {
3432 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3433 }
3434 return QVariant();
3435}
3436
3437static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3438{
3439 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3440 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3441 if ( parent->hasEvalError() )
3442 {
3443 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3444 return QVariant();
3445 }
3446 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3447 if ( parent->hasEvalError() )
3448 {
3449 return QVariant();
3450 }
3451 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3452 if ( parent->hasEvalError() )
3453 {
3454 return QVariant();
3455 }
3456 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3457 if ( parent->hasEvalError() )
3458 {
3459 return QVariant();
3460 }
3461 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3462
3463 const QgsMagneticModel model( name, filePath );
3464 try
3465 {
3466 double declination = 0;
3467 double Bx = 0;
3468 double By = 0;
3469 double Bz = 0;
3470 double Bxt = 0;
3471 double Byt = 0;
3472 double Bzt = 0;
3473
3474 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3475 {
3476 double H = 0;
3477 double F = 0;
3478 double D = 0;
3479 double I = 0;
3480 double Ht = 0;
3481 double Ft = 0;
3482 double Dt = 0;
3483 double It = 0;
3484 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3485 {
3486 return Dt;
3487 }
3488 else
3489 {
3490 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3491 }
3492 return declination;
3493 }
3494 else
3495 {
3496 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3497 }
3498 }
3499 catch ( QgsNotSupportedException &e )
3500 {
3501 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3502 }
3503 return QVariant();
3504}
3505
3506static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3507{
3508 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3509 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3510 if ( parent->hasEvalError() )
3511 {
3512 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3513 return QVariant();
3514 }
3515 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3516 if ( parent->hasEvalError() )
3517 {
3518 return QVariant();
3519 }
3520 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3521 if ( parent->hasEvalError() )
3522 {
3523 return QVariant();
3524 }
3525 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3526 if ( parent->hasEvalError() )
3527 {
3528 return QVariant();
3529 }
3530 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3531
3532 const QgsMagneticModel model( name, filePath );
3533 try
3534 {
3535 double declination = 0;
3536 double Bx = 0;
3537 double By = 0;
3538 double Bz = 0;
3539 double Bxt = 0;
3540 double Byt = 0;
3541 double Bzt = 0;
3542
3543 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3544 {
3545 double H = 0;
3546 double F = 0;
3547 double D = 0;
3548 double I = 0;
3549 double Ht = 0;
3550 double Ft = 0;
3551 double Dt = 0;
3552 double It = 0;
3553 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3554 {
3555 return It;
3556 }
3557 else
3558 {
3559 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3560 }
3561 return declination;
3562 }
3563 else
3565 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3566 }
3567 }
3568 catch ( QgsNotSupportedException &e )
3569 {
3570 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3571 }
3572 return QVariant();
3573}
3574
3575#define ENSURE_GEOM_TYPE( f, g, geomtype ) \
3576 if ( !( f ).hasGeometry() ) \
3577 return QVariant(); \
3578 QgsGeometry g = ( f ).geometry(); \
3579 if ( ( g ).type() != ( geomtype ) ) \
3580 return QVariant();
3581
3582static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3583{
3584 FEAT_FROM_CONTEXT( context, f )
3586 if ( g.isMultipart() )
3587 {
3588 return g.asMultiPoint().at( 0 ).x();
3589 }
3590 else
3591 {
3592 return g.asPoint().x();
3593 }
3594}
3595
3596static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3597{
3598 FEAT_FROM_CONTEXT( context, f )
3600 if ( g.isMultipart() )
3601 {
3602 return g.asMultiPoint().at( 0 ).y();
3603 }
3604 else
3605 {
3606 return g.asPoint().y();
3607 }
3608}
3609
3610static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3611{
3612 FEAT_FROM_CONTEXT( context, f )
3614
3615 if ( g.isEmpty() )
3616 return QVariant();
3617
3618 const QgsAbstractGeometry *abGeom = g.constGet();
3619
3620 if ( g.isEmpty() || !abGeom->is3D() )
3621 return QVariant();
3622
3623 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3624 {
3625 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3626 if ( point )
3627 return point->z();
3628 }
3629 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3630 {
3631 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3632 {
3633 if ( collection->numGeometries() > 0 )
3634 {
3635 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3636 return point->z();
3637 }
3638 }
3639 }
3640
3641 return QVariant();
3642}
3643
3644static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3645{
3646 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3647 if ( geom.isNull() )
3648 return QVariant();
3649
3650 bool isValid = geom.isGeosValid();
3651
3652 return QVariant( isValid );
3653}
3654
3655static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3656{
3657 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3658 if ( geom.isNull() )
3659 return QVariant();
3660
3661 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3662#if GEOS_VERSION_NUM < GEOS_COMPUTE_VERSION( 3, 10, 0 )
3664#else
3666#endif
3667 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3669 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3671
3672 const bool keepCollapsed = values.value( 2 ).toBool();
3673
3674 QgsGeometry valid;
3675 try
3676 {
3677 valid = geom.makeValid( method, keepCollapsed );
3678 }
3679 catch ( QgsNotSupportedException & )
3680 {
3681 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3682 return QVariant();
3683 }
3684
3685 return QVariant::fromValue( valid );
3686}
3687
3688static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3689{
3690 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3691 if ( geom.isNull() )
3692 return QVariant();
3693
3694 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3695 QVariantList array;
3696 for ( int i = 0; i < multiGeom.size(); ++i )
3697 {
3698 array += QVariant::fromValue( multiGeom.at( i ) );
3699 }
3700
3701 return array;
3702}
3703
3704static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3705{
3706 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3707 if ( geom.isNull() )
3708 return QVariant();
3709
3710 //if single point, return the point's x coordinate
3711 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3712 {
3713 return geom.asPoint().x();
3714 }
3715
3716 //otherwise return centroid x
3717 QgsGeometry centroid = geom.centroid();
3718 QVariant result( centroid.asPoint().x() );
3719 return result;
3720}
3721
3722static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3723{
3724 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3725 if ( geom.isNull() )
3726 return QVariant();
3727
3728 //if single point, return the point's y coordinate
3729 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3730 {
3731 return geom.asPoint().y();
3732 }
3733
3734 //otherwise return centroid y
3735 QgsGeometry centroid = geom.centroid();
3736 QVariant result( centroid.asPoint().y() );
3737 return result;
3738}
3739
3740static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3741{
3742 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3743 if ( geom.isNull() )
3744 return QVariant(); //or 0?
3745
3746 if ( !geom.constGet()->is3D() )
3747 return QVariant();
3748
3749 //if single point, return the point's z coordinate
3750 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3751 {
3753 if ( point )
3754 return point->z();
3755 }
3756 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3757 {
3759 {
3760 if ( collection->numGeometries() == 1 )
3761 {
3762 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3763 return point->z();
3764 }
3765 }
3766 }
3767
3768 return QVariant();
3769}
3770
3771static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3772{
3773 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3774 if ( geom.isNull() )
3775 return QVariant(); //or 0?
3776
3777 if ( !geom.constGet()->isMeasure() )
3778 return QVariant();
3779
3780 //if single point, return the point's m value
3781 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3782 {
3784 if ( point )
3785 return point->m();
3786 }
3787 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3788 {
3790 {
3791 if ( collection->numGeometries() == 1 )
3792 {
3793 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3794 return point->m();
3795 }
3796 }
3797 }
3798
3799 return QVariant();
3800}
3801
3802static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3803{
3804 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3805
3806 if ( geom.isNull() )
3807 return QVariant();
3808
3809 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3810
3811 if ( idx < 0 )
3812 {
3813 //negative idx
3814 int count = geom.constGet()->nCoordinates();
3815 idx = count + idx;
3816 }
3817 else
3818 {
3819 //positive idx is 1 based
3820 idx -= 1;
3821 }
3822
3823 QgsVertexId vId;
3824 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3825 {
3826 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3827 return QVariant();
3828 }
3829
3830 QgsPoint point = geom.constGet()->vertexAt( vId );
3831 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3832}
3833
3834static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3835{
3836 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3837
3838 if ( geom.isNull() )
3839 return QVariant();
3840
3841 QgsVertexId vId;
3842 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3843 {
3844 return QVariant();
3845 }
3846
3847 QgsPoint point = geom.constGet()->vertexAt( vId );
3848 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3849}
3850
3851static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3852{
3853 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3854
3855 if ( geom.isNull() )
3856 return QVariant();
3857
3858 QgsVertexId vId;
3859 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3860 {
3861 return QVariant();
3862 }
3863
3864 QgsPoint point = geom.constGet()->vertexAt( vId );
3865 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3866}
3867
3868static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3869{
3870 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3871
3872 if ( geom.isNull() )
3873 return QVariant();
3874
3875 bool ignoreClosing = false;
3876 if ( values.length() > 1 )
3877 {
3878 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3879 }
3880
3881 QgsMultiPoint *mp = new QgsMultiPoint();
3882
3883 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3884 for ( const QgsRingSequence &part : sequence )
3885 {
3886 for ( const QgsPointSequence &ring : part )
3887 {
3888 bool skipLast = false;
3889 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3890 {
3891 skipLast = true;
3892 }
3893
3894 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++i )
3895 {
3896 mp->addGeometry( ring.at( i ).clone() );
3897 }
3898 }
3899 }
3900
3901 return QVariant::fromValue( QgsGeometry( mp ) );
3902}
3903
3904static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3905{
3906 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3907
3908 if ( geom.isNull() )
3909 return QVariant();
3910
3911 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3912
3913 //OK, now we have a complete list of segmentized lines from the geometry
3915 for ( QgsLineString *line : linesToProcess )
3916 {
3917 for ( int i = 0; i < line->numPoints() - 1; ++i )
3918 {
3920 segment->setPoints( QgsPointSequence() << line->pointN( i ) << line->pointN( i + 1 ) );
3921 ml->addGeometry( segment );
3922 }
3923 delete line;
3924 }
3925
3926 return QVariant::fromValue( QgsGeometry( ml ) );
3927}
3928
3929static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3930{
3931 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3932
3933 if ( geom.isNull() )
3934 return QVariant();
3935
3937 if ( !curvePolygon && geom.isMultipart() )
3938 {
3940 {
3941 if ( collection->numGeometries() == 1 )
3942 {
3943 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3944 }
3945 }
3946 }
3947
3948 if ( !curvePolygon )
3949 return QVariant();
3950
3951 //idx is 1 based
3952 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3953
3954 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3955 return QVariant();
3956
3957 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3958 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3959 return result;
3960}
3961
3962static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3963{
3964 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3965
3966 if ( geom.isNull() )
3967 return QVariant();
3968
3970 if ( !collection )
3971 return QVariant();
3972
3973 //idx is 1 based
3974 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3975
3976 if ( idx < 0 || idx >= collection->numGeometries() )
3977 return QVariant();
3978
3979 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3980 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3981 return result;
3982}
3983
3984static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3985{
3986 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3987
3988 if ( geom.isNull() )
3989 return QVariant();
3990
3991 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3992 if ( !boundary )
3993 return QVariant();
3994
3995 return QVariant::fromValue( QgsGeometry( boundary ) );
3996}
3997
3998static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3999{
4000 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4001
4002 if ( geom.isNull() )
4003 return QVariant();
4004
4005 QgsGeometry merged = geom.mergeLines();
4006 if ( merged.isNull() )
4007 return QVariant();
4008
4009 return QVariant::fromValue( merged );
4010}
4011
4012static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4013{
4014 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4015 if ( geom.isNull() )
4016 return QVariant();
4017
4018 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4019 if ( geom2.isNull() )
4020 return QVariant();
4021
4022 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
4023 if ( sharedPaths.isNull() )
4024 return QVariant();
4025
4026 return QVariant::fromValue( sharedPaths );
4027}
4028
4029
4030static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4031{
4032 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4033
4034 if ( geom.isNull() )
4035 return QVariant();
4036
4037 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4038
4039 QgsGeometry simplified = geom.simplify( tolerance );
4040 if ( simplified.isNull() )
4041 return QVariant();
4042
4043 return simplified;
4044}
4045
4046static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4047{
4048 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4049
4050 if ( geom.isNull() )
4051 return QVariant();
4052
4053 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4054
4056
4057 QgsGeometry simplified = simplifier.simplify( geom );
4058 if ( simplified.isNull() )
4059 return QVariant();
4060
4061 return simplified;
4062}
4063
4064static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4065{
4066 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4067
4068 if ( geom.isNull() )
4069 return QVariant();
4070
4071 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
4072 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
4073 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4074 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
4075
4076 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
4077 if ( smoothed.isNull() )
4078 return QVariant();
4079
4080 return smoothed;
4081}
4082
4083static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4084{
4085 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4086
4087 if ( geom.isNull() )
4088 return QVariant();
4089
4090 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4091 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4092 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4093
4094 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
4095 if ( waved.isNull() )
4096 return QVariant();
4097
4098 return waved;
4099}
4100
4101static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4102{
4103 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4104
4105 if ( geom.isNull() )
4106 return QVariant();
4107
4108 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4109 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4110 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4111 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4112 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4113
4114 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4115 if ( waved.isNull() )
4116 return QVariant();
4117
4118 return waved;
4119}
4120
4121static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4122{
4123 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4124
4125 if ( geom.isNull() )
4126 return QVariant();
4127
4128 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4129 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4130 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4131
4132 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
4133 if ( waved.isNull() )
4134 return QVariant();
4135
4136 return waved;
4137}
4138
4139static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4140{
4141 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4142
4143 if ( geom.isNull() )
4144 return QVariant();
4145
4146 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4147 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4148 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4149 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4150 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4151
4152 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4153 if ( waved.isNull() )
4154 return QVariant();
4155
4156 return waved;
4157}
4158
4159static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4160{
4161 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4162
4163 if ( geom.isNull() )
4164 return QVariant();
4165
4166 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4167 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4168 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4169
4170 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
4171 if ( waved.isNull() )
4172 return QVariant();
4173
4174 return waved;
4175}
4176
4177static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4178{
4179 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4180
4181 if ( geom.isNull() )
4182 return QVariant();
4183
4184 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4185 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4186 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4187 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4188 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4189
4190 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4191 if ( waved.isNull() )
4192 return QVariant();
4193
4194 return waved;
4195}
4196
4197static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4198{
4199 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4200
4201 if ( geom.isNull() )
4202 return QVariant();
4203
4204 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4205 QVector< double > dashPattern;
4206 dashPattern.reserve( pattern.size() );
4207 for ( const QVariant &value : std::as_const( pattern ) )
4208 {
4209 bool ok = false;
4210 double v = value.toDouble( &ok );
4211 if ( ok )
4212 {
4213 dashPattern << v;
4214 }
4215 else
4216 {
4217 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4218 return QgsGeometry();
4219 }
4220 }
4221
4222 if ( dashPattern.size() % 2 != 0 )
4223 {
4224 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4225 return QgsGeometry();
4226 }
4227
4228 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4230 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4232 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4234 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4236 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4238 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4240 else
4241 {
4242 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4243 return QgsGeometry();
4244 }
4245
4246 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4248 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4250 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4252 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4254 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4256 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4258 else
4259 {
4260 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4261 return QgsGeometry();
4262 }
4263
4264 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4266 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4268 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4270 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4272 else
4273 {
4274 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4275 return QgsGeometry();
4276 }
4277
4278 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4279
4280 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4281 if ( result.isNull() )
4282 return QVariant();
4283
4284 return result;
4285}
4286
4287static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4288{
4289 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4290
4291 if ( geom.isNull() )
4292 return QVariant();
4293
4294 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4295 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4296 if ( densified.isNull() )
4297 return QVariant();
4298
4299 return densified;
4300}
4301
4302static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4303{
4304 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4305
4306 if ( geom.isNull() )
4307 return QVariant();
4308
4309 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4310 const QgsGeometry densified = geom.densifyByDistance( distance );
4311 if ( densified.isNull() )
4312 return QVariant();
4313
4314 return densified;
4315}
4316
4317static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4318{
4319 QVariantList list;
4320 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4321 {
4322 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4323 }
4324 else
4325 {
4326 list = values;
4327 }
4328
4329 QVector< QgsGeometry > parts;
4330 parts.reserve( list.size() );
4331 for ( const QVariant &value : std::as_const( list ) )
4332 {
4333 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4334 if ( part.isNull() )
4335 return QgsGeometry();
4336 parts << part;
4337 }
4338
4339 return QgsGeometry::collectGeometry( parts );
4340}
4341
4342static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4343{
4344 if ( values.count() < 2 || values.count() > 4 )
4345 {
4346 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4347 return QVariant();
4348 }
4349
4350 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4351 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4352 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4353 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4354 switch ( values.count() )
4355 {
4356 case 2:
4357 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4358 case 3:
4359 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4360 case 4:
4361 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4362 }
4363 return QVariant(); //avoid warning
4364}
4365
4366static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4367{
4368 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4369 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4370 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4371 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4372}
4373
4374static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4375{
4376 if ( values.empty() )
4377 {
4378 return QVariant();
4379 }
4380
4381 QVector<QgsPoint> points;
4382 points.reserve( values.count() );
4383
4384 auto addPoint = [&points]( const QgsGeometry &geom ) {
4385 if ( geom.isNull() )
4386 return;
4387
4388 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4389 return;
4390
4392 if ( !point )
4393 return;
4394
4395 points << *point;
4396 };
4397
4398 for ( const QVariant &value : values )
4399 {
4400 if ( value.userType() == QMetaType::Type::QVariantList )
4401 {
4402 const QVariantList list = value.toList();
4403 for ( const QVariant &v : list )
4404 {
4405 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4406 }
4407 }
4408 else
4409 {
4410 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4411 }
4412 }
4413
4414 if ( points.count() < 2 )
4415 return QVariant();
4416
4417 return QgsGeometry( new QgsLineString( points ) );
4418}
4419
4420static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4421{
4422 if ( values.count() < 1 )
4423 {
4424 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4425 return QVariant();
4426 }
4427
4428 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4429
4430 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4431 return outerRing; // if it's already a polygon we have nothing to do
4432
4433 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4434 return QVariant();
4435
4436 auto polygon = std::make_unique< QgsPolygon >();
4437
4438 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4439 if ( !exteriorRing && outerRing.isMultipart() )
4440 {
4442 {
4443 if ( collection->numGeometries() == 1 )
4444 {
4445 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4446 }
4447 }
4448 }
4449
4450 if ( !exteriorRing )
4451 return QVariant();
4452
4453 polygon->setExteriorRing( exteriorRing->segmentize() );
4454
4455
4456 for ( int i = 1; i < values.count(); ++i )
4457 {
4458 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4459 if ( ringGeom.isNull() )
4460 continue;
4461
4462 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4463 continue;
4464
4465 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4466 if ( !ring && ringGeom.isMultipart() )
4467 {
4469 {
4470 if ( collection->numGeometries() == 1 )
4471 {
4472 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4473 }
4474 }
4475 }
4476
4477 if ( !ring )
4478 continue;
4479
4480 polygon->addInteriorRing( ring->segmentize() );
4481 }
4482
4483 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4484}
4485
4486static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4487{
4488 auto tr = std::make_unique<QgsTriangle>();
4489 auto lineString = std::make_unique<QgsLineString>();
4490 lineString->clear();
4491
4492 for ( const QVariant &value : values )
4493 {
4494 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4495 if ( geom.isNull() )
4496 return QVariant();
4497
4498 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4499 return QVariant();
4500
4502 if ( !point && geom.isMultipart() )
4503 {
4505 {
4506 if ( collection->numGeometries() == 1 )
4507 {
4508 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4509 }
4510 }
4511 }
4512
4513 if ( !point )
4514 return QVariant();
4515
4516 lineString->addVertex( *point );
4517 }
4518
4519 tr->setExteriorRing( lineString.release() );
4520
4521 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4522}
4523
4524static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4525{
4526 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4527 if ( geom.isNull() )
4528 return QVariant();
4529
4530 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4531 return QVariant();
4532
4533 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4534 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4535
4536 if ( segment < 3 )
4537 {
4538 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4539 return QVariant();
4540 }
4542 if ( !point && geom.isMultipart() )
4543 {
4545 {
4546 if ( collection->numGeometries() == 1 )
4547 {
4548 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4549 }
4550 }
4551 }
4552 if ( !point )
4553 return QVariant();
4554
4555 QgsCircle circ( *point, radius );
4556 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4557}
4558
4559static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4560{
4561 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4562 if ( geom.isNull() )
4563 return QVariant();
4564
4565 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4566 return QVariant();
4567
4568 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4569 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4570 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4571 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4572 if ( segment < 3 )
4573 {
4574 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4575 return QVariant();
4576 }
4578 if ( !point && geom.isMultipart() )
4579 {
4581 {
4582 if ( collection->numGeometries() == 1 )
4583 {
4584 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4585 }
4586 }
4587 }
4588 if ( !point )
4589 return QVariant();
4590
4591 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4592 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4593}
4594
4595static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4596{
4597 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4598 if ( pt1.isNull() )
4599 return QVariant();
4600
4601 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4602 return QVariant();
4603
4604 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4605 if ( pt2.isNull() )
4606 return QVariant();
4607
4608 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4609 return QVariant();
4610
4611 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4612 if ( nbEdges < 3 )
4613 {
4614 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4615 return QVariant();
4616 }
4617
4618 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4620 {
4621 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4622 return QVariant();
4623 }
4624
4626 if ( !center && pt1.isMultipart() )
4627 {
4629 {
4630 if ( collection->numGeometries() == 1 )
4631 {
4632 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4633 }
4634 }
4635 }
4636 if ( !center )
4637 return QVariant();
4638
4640 if ( !corner && pt2.isMultipart() )
4641 {
4643 {
4644 if ( collection->numGeometries() == 1 )
4645 {
4646 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4647 }
4648 }
4649 }
4650 if ( !corner )
4651 return QVariant();
4652
4653 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4654
4655 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4656}
4657
4658static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4659{
4660 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4661 if ( pt1.isNull() )
4662 return QVariant();
4663 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4664 return QVariant();
4665
4666 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4667 if ( pt2.isNull() )
4668 return QVariant();
4669 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4670 return QVariant();
4671
4672 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4673 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4674 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4675
4676 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4677}
4678
4679static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4680{
4681 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4682 if ( pt1.isNull() )
4683 return QVariant();
4684 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4685 return QVariant();
4686
4687 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4688 if ( pt2.isNull() )
4689 return QVariant();
4690 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4691 return QVariant();
4692
4693 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4694 if ( pt3.isNull() )
4695 return QVariant();
4696 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4697 return QVariant();
4698
4699 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4700 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4701 {
4702 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4703 return QVariant();
4704 }
4705 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4706 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4707 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4708 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4709 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4710}
4711
4712static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4713{
4714 if ( geom.isNull() )
4715 return QVariant();
4716
4717 if ( idx < 0 )
4718 {
4719 idx += geom.constGet()->nCoordinates();
4720 }
4721 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4722 {
4723 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4724 return QVariant();
4725 }
4726 return QVariant::fromValue( geom.vertexAt( idx ) );
4727}
4728
4729// function used for the old $ style
4730static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4731{
4732 FEAT_FROM_CONTEXT( context, feature )
4733 const QgsGeometry geom = feature.geometry();
4734 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4735
4736 const QVariant v = pointAt( geom, idx, parent );
4737
4738 if ( !v.isNull() )
4739 return QVariant( v.value<QgsPoint>().x() );
4740 else
4741 return QVariant();
4742}
4743static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4744{
4745 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))
4746 {
4747 return fcnOldXat( values, f, parent, node );
4748 }
4749 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)
4750 {
4751 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4752 }
4753
4754 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4755 if ( geom.isNull() )
4756 {
4757 return QVariant();
4758 }
4759
4760 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4761
4762 const QVariant v = pointAt( geom, vertexNumber, parent );
4763 if ( !v.isNull() )
4764 return QVariant( v.value<QgsPoint>().x() );
4765 else
4766 return QVariant();
4767}
4768
4769// function used for the old $ style
4770static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4771{
4772 FEAT_FROM_CONTEXT( context, feature )
4773 const QgsGeometry geom = feature.geometry();
4774 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4775
4776 const QVariant v = pointAt( geom, idx, parent );
4777
4778 if ( !v.isNull() )
4779 return QVariant( v.value<QgsPoint>().y() );
4780 else
4781 return QVariant();
4782}
4783static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4784{
4785 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))
4786 {
4787 return fcnOldYat( values, f, parent, node );
4788 }
4789 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)
4790 {
4791 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4792 }
4793
4794 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4795 if ( geom.isNull() )
4796 {
4797 return QVariant();
4798 }
4799
4800 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4801
4802 const QVariant v = pointAt( geom, vertexNumber, parent );
4803 if ( !v.isNull() )
4804 return QVariant( v.value<QgsPoint>().y() );
4805 else
4806 return QVariant();
4807}
4808
4809static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4810{
4811 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4812 if ( geom.isNull() )
4813 {
4814 return QVariant();
4815 }
4816
4817 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4818
4819 const QVariant v = pointAt( geom, vertexNumber, parent );
4820 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4821 return QVariant( v.value<QgsPoint>().z() );
4822 else
4823 return QVariant();
4824}
4825
4826static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4827{
4828 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4829 if ( geom.isNull() )
4830 {
4831 return QVariant();
4832 }
4833
4834 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4835
4836 const QVariant v = pointAt( geom, vertexNumber, parent );
4837 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4838 return QVariant( v.value<QgsPoint>().m() );
4839 else
4840 return QVariant();
4841}
4842
4843
4844static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4845{
4846 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4847 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4848 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4849 return result;
4850}
4851
4852static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4853{
4854 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4855 if ( wkb.isNull() )
4856 return QVariant();
4857
4858 QgsGeometry geom;
4859 geom.fromWkb( wkb );
4860 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4861}
4862
4863static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4864{
4865 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4866 QgsOgcUtils::Context ogcContext;
4867 if ( context )
4868 {
4869 QgsWeakMapLayerPointer mapLayerPtr { context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4870 if ( mapLayerPtr )
4871 {
4872 ogcContext.layer = mapLayerPtr.data();
4873 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4874 }
4875 }
4876 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4877 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4878 return result;
4879}
4880
4881static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4882{
4883 FEAT_FROM_CONTEXT( context, f )
4885 QgsDistanceArea *calc = parent->geomCalculator();
4886 if ( calc )
4887 {
4888 try
4889 {
4890 double area = calc->measureArea( f.geometry() );
4891 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4892 return QVariant( area );
4893 }
4894 catch ( QgsCsException & )
4895 {
4896 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4897 return QVariant();
4898 }
4899 }
4900 else
4901 {
4902 return QVariant( f.geometry().area() );
4903 }
4904}
4905
4906static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4907{
4908 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4909
4910 if ( geom.type() != Qgis::GeometryType::Polygon )
4911 return QVariant();
4912
4913 return QVariant( geom.area() );
4914}
4915
4916static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4917{
4918 FEAT_FROM_CONTEXT( context, f )
4920 QgsDistanceArea *calc = parent->geomCalculator();
4921 if ( calc )
4922 {
4923 try
4924 {
4925 double len = calc->measureLength( f.geometry() );
4926 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4927 return QVariant( len );
4928 }
4929 catch ( QgsCsException & )
4930 {
4931 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4932 return QVariant();
4933 }
4934 }
4935 else
4936 {
4937 return QVariant( f.geometry().length() );
4938 }
4939}
4940
4941static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4942{
4943 FEAT_FROM_CONTEXT( context, f )
4945 QgsDistanceArea *calc = parent->geomCalculator();
4946 if ( calc )
4947 {
4948 try
4949 {
4950 double len = calc->measurePerimeter( f.geometry() );
4951 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4952 return QVariant( len );
4953 }
4954 catch ( QgsCsException & )
4955 {
4956 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4957 return QVariant();
4958 }
4959 }
4960 else
4961 {
4962 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4963 }
4964}
4965
4966static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4967{
4968 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4969
4970 if ( geom.type() != Qgis::GeometryType::Polygon )
4971 return QVariant();
4972
4973 //length for polygons = perimeter
4974 return QVariant( geom.length() );
4975}
4976
4977static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4978{
4979 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4980 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4981}
4982
4983static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4984{
4985 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4986 if ( geom.isNull() )
4987 return QVariant();
4988
4989 return QVariant( geom.constGet()->partCount() );
4990}
4991
4992static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4993{
4994 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4995 if ( geom.isNull() )
4996 return QVariant();
4997
4998 return QVariant( geom.isMultipart() );
4999}
5000
5001static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5002{
5003 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5004
5005 if ( geom.isNull() )
5006 return QVariant();
5007
5009 if ( curvePolygon )
5010 return QVariant( curvePolygon->numInteriorRings() );
5011
5013 if ( collection )
5014 {
5015 //find first CurvePolygon in collection
5016 for ( int i = 0; i < collection->numGeometries(); ++i )
5017 {
5018 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5019 if ( !curvePolygon )
5020 continue;
5021
5022 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
5023 }
5024 }
5025
5026 return QVariant();
5027}
5028
5029static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5030{
5031 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5032
5033 if ( geom.isNull() )
5034 return QVariant();
5035
5037 if ( curvePolygon )
5038 return QVariant( curvePolygon->ringCount() );
5039
5040 bool foundPoly = false;
5041 int ringCount = 0;
5043 if ( collection )
5044 {
5045 //find CurvePolygons in collection
5046 for ( int i = 0; i < collection->numGeometries(); ++i )
5047 {
5048 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5049 if ( !curvePolygon )
5050 continue;
5051
5052 foundPoly = true;
5053 ringCount += curvePolygon->ringCount();
5054 }
5055 }
5056
5057 if ( !foundPoly )
5058 return QVariant();
5059
5060 return QVariant( ringCount );
5061}
5062
5063static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5064{
5065 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5066 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
5067 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
5068 return result;
5069}
5070
5071static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5072{
5073 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5074 return QVariant::fromValue( geom.boundingBox().width() );
5075}
5076
5077static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5078{
5079 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5080 return QVariant::fromValue( geom.boundingBox().height() );
5081}
5082
5083static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5084{
5085 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5086 if ( geom.isNull() )
5087 return QVariant();
5088
5090}
5091
5092static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5093{
5094 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5095 return QVariant::fromValue( geom.boundingBox().xMinimum() );
5096}
5097
5098static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5099{
5100 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5101 return QVariant::fromValue( geom.boundingBox().xMaximum() );
5102}
5103
5104static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5105{
5106 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5107 return QVariant::fromValue( geom.boundingBox().yMinimum() );
5108}
5109
5110static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5111{
5112 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5113 return QVariant::fromValue( geom.boundingBox().yMaximum() );
5114}
5115
5116static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5117{
5118 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5119
5120 if ( geom.isNull() || geom.isEmpty() )
5121 return QVariant();
5122
5123 if ( !geom.constGet()->is3D() )
5124 return QVariant();
5125
5126 double max = std::numeric_limits< double >::lowest();
5127
5128 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5129 {
5130 double z = ( *it ).z();
5131
5132 if ( max < z )
5133 max = z;
5134 }
5135
5136 if ( max == std::numeric_limits< double >::lowest() )
5137 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5138
5139 return QVariant( max );
5140}
5141
5142static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5143{
5144 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5145
5146 if ( geom.isNull() || geom.isEmpty() )
5147 return QVariant();
5148
5149 if ( !geom.constGet()->is3D() )
5150 return QVariant();
5151
5152 double min = std::numeric_limits< double >::max();
5153
5154 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5155 {
5156 double z = ( *it ).z();
5157
5158 if ( z < min )
5159 min = z;
5160 }
5161
5162 if ( min == std::numeric_limits< double >::max() )
5163 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5164
5165 return QVariant( min );
5166}
5167
5168static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5169{
5170 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5171
5172 if ( geom.isNull() || geom.isEmpty() )
5173 return QVariant();
5174
5175 if ( !geom.constGet()->isMeasure() )
5176 return QVariant();
5177
5178 double min = std::numeric_limits< double >::max();
5179
5180 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5181 {
5182 double m = ( *it ).m();
5183
5184 if ( m < min )
5185 min = m;
5186 }
5187
5188 if ( min == std::numeric_limits< double >::max() )
5189 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5190
5191 return QVariant( min );
5192}
5193
5194static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5195{
5196 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5197
5198 if ( geom.isNull() || geom.isEmpty() )
5199 return QVariant();
5200
5201 if ( !geom.constGet()->isMeasure() )
5202 return QVariant();
5203
5204 double max = std::numeric_limits< double >::lowest();
5205
5206 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5207 {
5208 double m = ( *it ).m();
5209
5210 if ( max < m )
5211 max = m;
5212 }
5213
5214 if ( max == std::numeric_limits< double >::lowest() )
5215 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5216
5217 return QVariant( max );
5218}
5219
5220static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5221{
5222 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5224 if ( !curve )
5225 {
5226 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5227 return QVariant();
5228 }
5229
5230 return QVariant( curve->sinuosity() );
5231}
5232
5233static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5234{
5235 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5236 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5237 if ( !curve )
5238 {
5239 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5240 return QVariant();
5241 }
5242
5243 return QVariant( curve->straightDistance2d() );
5244}
5245
5246static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5247{
5248 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5250
5251 if ( !poly )
5252 {
5253 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5254 return QVariant();
5255 }
5256
5257 return QVariant( poly->roundness() );
5258}
5259
5260
5261static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5262{
5263 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5264 if ( geom.isNull() )
5265 return QVariant();
5266
5267 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5268 flipped->swapXy();
5269 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5270}
5271
5272static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5273{
5274 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5275 if ( fGeom.isNull() )
5276 return QVariant();
5277
5278 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5279 if ( !curve && fGeom.isMultipart() )
5280 {
5282 {
5283 if ( collection->numGeometries() == 1 )
5284 {
5285 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5286 }
5287 }
5288 }
5289
5290 if ( !curve )
5291 return QVariant();
5292
5293 return QVariant::fromValue( curve->isClosed() );
5294}
5295
5296static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5297{
5298 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5299
5300 if ( geom.isNull() )
5301 return QVariant();
5302
5303 QVariant result;
5304 if ( !geom.isMultipart() )
5305 {
5307
5308 if ( !line )
5309 return QVariant();
5310
5311 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5312 closedLine->close();
5313
5314 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5315 }
5316 else
5317 {
5319 if ( !collection )
5320 return QVariant();
5321
5322 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5323
5324 for ( int i = 0; i < collection->numGeometries(); ++i )
5325 {
5326 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5327 {
5328 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5329 closedLine->close();
5330
5331 closed->addGeometry( closedLine.release() );
5332 }
5333 }
5334 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5335 }
5336
5337 return result;
5338}
5339
5340static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5341{
5342 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5343 if ( fGeom.isNull() )
5344 return QVariant();
5345
5346 return QVariant::fromValue( fGeom.isEmpty() );
5347}
5348
5349static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5350{
5351 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5352 return QVariant::fromValue( true );
5353
5354 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5355 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5356}
5357
5358static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5359{
5360 if ( values.length() < 2 || values.length() > 3 )
5361 return QVariant();
5362
5363 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5364 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5365
5366 if ( fGeom.isNull() || sGeom.isNull() )
5367 return QVariant();
5368
5369 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5370
5371 if ( values.length() == 2 )
5372 {
5373 //two geometry arguments, return relation
5374 QString result = engine->relate( sGeom.constGet() );
5375 return QVariant::fromValue( result );
5376 }
5377 else
5378 {
5379 //three arguments, test pattern
5380 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5381 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5382 return QVariant::fromValue( result );
5383 }
5384}
5385
5386static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5387{
5388 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5389 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5390 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5391}
5392static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5393{
5394 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5395 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5396 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5397}
5398static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5399{
5400 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5401 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5402 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5403}
5404static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5405{
5406 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5407 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5408 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5409}
5410static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5411{
5412 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5413 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5414 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5415}
5416static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5417{
5418 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5419 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5420 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5421}
5422static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5423{
5424 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5425 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5426 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5427}
5428static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5429{
5430 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5431 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5432 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5433}
5434
5435static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5436{
5437 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5438 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5439 return fGeom.isExactlyEqual( sGeom ) ? TVL_True : TVL_False;
5440}
5441
5442static QVariant fcnIsEqualsExact( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5443{
5444 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5445 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5446 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5447
5448 bool ok;
5449 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5450 if ( !ok )
5451 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5452
5453 QVariant ret = TVL_False;
5454 try
5455 {
5456 ret = fGeom.isExactlyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5457 }
5458 catch ( QgsNotSupportedException &e )
5459 {
5460 SET_EVAL_ERROR( e.what() );
5461 }
5462 return ret;
5463}
5464
5465static QVariant fcnIsEqualsTopological( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5466{
5467 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5468 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5469 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5470
5471 bool ok;
5472 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
5473 if ( !ok )
5474 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5475
5476 QVariant ret = TVL_False;
5477 try
5478 {
5479 ret = fGeom.isTopologicallyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5480 }
5481 catch ( QgsNotSupportedException &e )
5482 {
5483 SET_EVAL_ERROR( e.what() );
5484 }
5485 return ret;
5486}
5487
5488static QVariant fcnIsEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5489{
5490 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5491 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5492 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5493
5494 bool ok;
5495 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5496 if ( !ok )
5497 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5498
5499 double epsilon = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5500
5501 QVariant ret = TVL_False;
5502 try
5503 {
5504 ret = fGeom.isFuzzyEqual( sGeom, epsilon, backend ) ? TVL_True : TVL_False;
5505 }
5506 catch ( QgsNotSupportedException &e )
5507 {
5508 SET_EVAL_ERROR( e.what() );
5509 }
5510 return ret;
5511}
5512
5513static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5514{
5515 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5516 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5517 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5518 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5519 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5520 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5521
5523 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5524 capStyle = Qgis::EndCapStyle::Flat;
5525 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5526 capStyle = Qgis::EndCapStyle::Square;
5527
5529 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5530 joinStyle = Qgis::JoinStyle::Miter;
5531 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5532 joinStyle = Qgis::JoinStyle::Bevel;
5533
5534 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5535 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5536 return result;
5537}
5538
5539static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5540{
5541 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5542 const QgsGeometry reoriented = fGeom.forceRHR();
5543 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5544}
5545
5546static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5547{
5548 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5549 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5550 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5551}
5552
5553static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5554{
5555 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5556 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5557 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5558}
5559
5560static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5561{
5562 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5564 if ( !pt && fGeom.isMultipart() )
5565 {
5567 {
5568 if ( collection->numGeometries() == 1 )
5569 {
5570 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5571 }
5572 }
5573 }
5574
5575 if ( !pt )
5576 {
5577 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5578 return QVariant();
5579 }
5580
5581 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5582 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5583 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5584 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5585
5586 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5587 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5588 return result;
5589}
5590
5591static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5592{
5593 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5594 if ( fGeom.type() != Qgis::GeometryType::Line )
5595 {
5596 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5597 return QVariant();
5598 }
5599
5600 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5601 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5602 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5603
5604 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5605 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5606 return result;
5607}
5608
5609static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5610{
5611 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5612 if ( fGeom.type() != Qgis::GeometryType::Line )
5613 {
5614 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5615 return QVariant();
5616 }
5617
5618 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5619
5620 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5621 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5622 return result;
5623}
5624
5625static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5626{
5627 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5628 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5629 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5630 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5631 if ( joinInt < 1 || joinInt > 3 )
5632 return QVariant();
5633 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5634
5635 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5636
5637 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5638 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5639 return result;
5640}
5641
5642static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5643{
5644 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5645 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5646 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5647
5648 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5649 if ( joinInt < 1 || joinInt > 3 )
5650 return QVariant();
5651 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5652
5653 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5654
5655 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5656 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5657 return result;
5658}
5659
5660static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5661{
5662 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5663 const double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5664 const double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5665
5666 const double deflectionStart = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5667 const double deflectionEnd = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5668
5669 QgsGeometry geom = fGeom.extendLine( distStart, distEnd, deflectionStart, deflectionEnd );
5670 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5671 return result;
5672}
5673
5674static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5675{
5676 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5677 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5678 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5679 fGeom.translate( dx, dy );
5680 return QVariant::fromValue( fGeom );
5681}
5682
5683static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5684{
5685 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5686 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5687 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent ) : QgsGeometry();
5688 const bool perPart = values.value( 3 ).toBool();
5689
5690 if ( center.isNull() && perPart && fGeom.isMultipart() )
5691 {
5692 // no explicit center, rotating per part
5693 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5694 // the result is equivalent to setting perPart as false anyway)
5695 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5696 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5697 {
5698 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5699 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5700 t.rotate( -rotation );
5701 t.translate( -partCenter.x(), -partCenter.y() );
5702 ( *it )->transform( t );
5703 }
5704 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5705 }
5706 else
5707 {
5708 QgsPointXY pt;
5709 if ( center.isEmpty() )
5710 {
5711 // if center wasn't specified, use bounding box centroid
5712 pt = fGeom.boundingBox().center();
5713 }
5715 {
5716 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5717 return QVariant();
5718 }
5719 else
5720 {
5722 }
5723
5724 fGeom.rotate( rotation, pt );
5725 return QVariant::fromValue( fGeom );
5726 }
5727}
5728
5729static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5730{
5731 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5732 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5733 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5734 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent ) : QgsGeometry();
5735
5736 QgsPointXY pt;
5737 if ( center.isNull() )
5738 {
5739 // if center wasn't specified, use bounding box centroid
5740 pt = fGeom.boundingBox().center();
5741 }
5743 {
5744 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5745 return QVariant();
5746 }
5747 else
5748 {
5749 pt = center.asPoint();
5750 }
5751
5752 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5753 t.scale( xScale, yScale );
5754 t.translate( -pt.x(), -pt.y() );
5755 fGeom.transform( t );
5756 return QVariant::fromValue( fGeom );
5757}
5758
5759static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5760{
5761 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5762 if ( fGeom.isNull() )
5763 {
5764 return QVariant();
5765 }
5766
5767 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5768 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5769
5770 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5771
5772 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5773 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5774
5775 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5776 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5777 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5778 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5779
5780 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5781 {
5782 fGeom.get()->addZValue( 0 );
5783 }
5784 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5785 {
5786 fGeom.get()->addMValue( 0 );
5787 }
5788
5789 QTransform transform;
5790 transform.translate( deltaX, deltaY );
5791 transform.rotate( rotationZ );
5792 transform.scale( scaleX, scaleY );
5793 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5794
5795 return QVariant::fromValue( fGeom );
5796}
5797
5798
5799static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5800{
5801 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5802 QgsGeometry geom = fGeom.centroid();
5803 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5804 return result;
5805}
5806static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5807{
5808 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5809 QgsGeometry geom = fGeom.pointOnSurface();
5810 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5811 return result;
5812}
5813
5814static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5815{
5816 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5817 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5818 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5819 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5820 return result;
5821}
5822
5823static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5824{
5825 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5826 QgsGeometry geom = fGeom.convexHull();
5827 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5828 return result;
5829}
5830
5831#if GEOS_VERSION_NUM >= GEOS_COMPUTE_VERSION( 3, 11, 0 )
5832static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5833{
5834 try
5835 {
5836 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5837 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5838 const bool allowHoles = values.value( 2 ).toBool();
5839 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5840 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5841 return result;
5842 }
5843 catch ( QgsCsException &cse )
5844 {
5845 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5846 return QVariant();
5847 }
5848}
5849#endif
5850
5851static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5852{
5853 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5854 int segments = 36;
5855 if ( values.length() == 2 )
5856 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5857 if ( segments < 0 )
5858 {
5859 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5860 return QVariant();
5861 }
5862
5863 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5864 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5865 return result;
5866}
5867
5868static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5869{
5870 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5872 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5873 return result;
5874}
5875
5876static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5877{
5878 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5879
5880 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5881 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5882 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5883
5884 double area, angle, width, height;
5885 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5886
5887 if ( geom.isNull() )
5888 {
5889 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5890 return QVariant();
5891 }
5892 return angle;
5893}
5894
5895static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5896{
5897 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5898 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5899 QgsGeometry geom = fGeom.difference( sGeom );
5900 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5901 return result;
5902}
5903
5904static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5905{
5906 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5907 return QVariant();
5908
5909 // two variants, one for geometry, one for string
5910
5911 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5912 if ( !fGeom.isNull() )
5913 {
5914 QVariant result;
5915 if ( !fGeom.isMultipart() )
5916 {
5917 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5918 if ( !curve )
5919 return QVariant();
5920
5921 QgsCurve *reversed = curve->reversed();
5922 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5923 }
5924 else
5925 {
5927 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5928 for ( int i = 0; i < collection->numGeometries(); ++i )
5929 {
5930 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5931 {
5932 reversed->addGeometry( curve->reversed() );
5933 }
5934 else
5935 {
5936 reversed->addGeometry( collection->geometryN( i )->clone() );
5937 }
5938 }
5939 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5940 }
5941 return result;
5942 }
5943
5944 //fall back to string variant
5945 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5946 std::reverse( string.begin(), string.end() );
5947 return string;
5948}
5949
5950static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5951{
5952 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5953 if ( fGeom.isNull() )
5954 return QVariant();
5955
5957 if ( !curvePolygon && fGeom.isMultipart() )
5958 {
5960 {
5961 if ( collection->numGeometries() == 1 )
5962 {
5963 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5964 }
5965 }
5966 }
5967
5968 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5969 return QVariant();
5970
5971 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5972 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5973 return result;
5974}
5975
5976static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5977{
5978 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5979 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5980 return QVariant( fGeom.distance( sGeom ) );
5981}
5982
5983static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5984{
5985 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5986 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5987
5988 double res = -1;
5989 if ( values.length() == 3 && values.at( 2 ).isValid() )
5990 {
5991 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5992 densify = std::clamp( densify, 0.0, 1.0 );
5993 res = g1.hausdorffDistanceDensify( g2, densify );
5994 }
5995 else
5996 {
5997 res = g1.hausdorffDistance( g2 );
5998 }
5999
6000 return res > -1 ? QVariant( res ) : QVariant();
6001}
6002
6003static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6004{
6005 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6006 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6007 QgsGeometry geom = fGeom.intersection( sGeom );
6008 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6009 return result;
6010}
6011static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6012{
6013 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6014 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6015 QgsGeometry geom = fGeom.symDifference( sGeom );
6016 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6017 return result;
6018}
6019static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6020{
6021 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6022 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6023 QgsGeometry geom = fGeom.combine( sGeom );
6024 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6025 return result;
6026}
6027
6028static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6029{
6030 if ( values.length() < 1 || values.length() > 2 )
6031 return QVariant();
6032
6033 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6034 int prec = 8;
6035 if ( values.length() == 2 )
6036 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6037 QString wkt = fGeom.asWkt( prec );
6038 return QVariant( wkt );
6039}
6040
6041static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6042{
6043 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6044 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
6045}
6046
6047static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6048{
6049 if ( values.length() != 2 )
6050 {
6051 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
6052 return QVariant();
6053 }
6054
6055 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6056 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6057
6058 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6059 if ( !pt1 && fGeom1.isMultipart() )
6060 {
6062 {
6063 if ( collection->numGeometries() == 1 )
6064 {
6065 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6066 }
6067 }
6068 }
6069
6070 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6071 if ( !pt2 && fGeom2.isMultipart() )
6072 {
6074 {
6075 if ( collection->numGeometries() == 1 )
6076 {
6077 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6078 }
6079 }
6080 }
6081
6082 if ( !pt1 || !pt2 )
6083 {
6084 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
6085 return QVariant();
6086 }
6087
6088 // Code from PostGIS
6089 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
6090 {
6091 if ( pt1->y() < pt2->y() )
6092 return 0.0;
6093 else if ( pt1->y() > pt2->y() )
6094 return M_PI;
6095 else
6096 return 0;
6097 }
6098
6099 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
6100 {
6101 if ( pt1->x() < pt2->x() )
6102 return M_PI_2;
6103 else if ( pt1->x() > pt2->x() )
6104 return M_PI + ( M_PI_2 );
6105 else
6106 return 0;
6107 }
6108
6109 if ( pt1->x() < pt2->x() )
6110 {
6111 if ( pt1->y() < pt2->y() )
6112 {
6113 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
6114 }
6115 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
6116 {
6117 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI_2 );
6118 }
6119 }
6120
6121 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
6122 {
6123 if ( pt1->y() > pt2->y() )
6124 {
6125 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI;
6126 }
6127 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
6128 {
6129 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI_2 ) );
6130 }
6131 }
6132}
6133
6134static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6135{
6136 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6137 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6138 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6139 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
6140
6141 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
6142 {
6143 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
6144 return QVariant();
6145 }
6146
6147 const QgsPointXY point1 = geom1.asPoint();
6148 const QgsPointXY point2 = geom2.asPoint();
6149 if ( point1.isEmpty() || point2.isEmpty() )
6150 {
6151 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
6152 return QVariant();
6153 }
6154
6156 if ( context )
6157 {
6158 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6159
6160 if ( !sourceCrs.isValid() )
6161 {
6162 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
6163 }
6164
6165 if ( ellipsoid.isEmpty() )
6166 {
6167 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
6168 }
6169 }
6170
6171 if ( !sourceCrs.isValid() )
6172 {
6173 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
6174 return QVariant();
6175 }
6176
6177 QgsDistanceArea da;
6178 da.setSourceCrs( sourceCrs, tContext );
6179 if ( !da.setEllipsoid( ellipsoid ) )
6180 {
6181 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
6182 return QVariant();
6183 }
6184
6185 try
6186 {
6187 const double bearing = da.bearing( point1, point2 );
6188 if ( std::isfinite( bearing ) )
6189 {
6190 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
6191 }
6192 }
6193 catch ( QgsCsException &cse )
6194 {
6195 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
6196 return QVariant();
6197 }
6198 return QVariant();
6199}
6200
6201static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6202{
6203 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6204
6206 {
6207 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
6208 return QVariant();
6209 }
6210
6211 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6212 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6213 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
6214
6215 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef() );
6216 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
6217
6218 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
6219}
6220
6221static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6222{
6223 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6224 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6225
6226 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6227 if ( !pt1 && fGeom1.isMultipart() )
6228 {
6230 {
6231 if ( collection->numGeometries() == 1 )
6232 {
6233 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6234 }
6235 }
6236 }
6237 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6238 if ( !pt2 && fGeom2.isMultipart() )
6239 {
6241 {
6242 if ( collection->numGeometries() == 1 )
6243 {
6244 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6245 }
6246 }
6247 }
6248
6249 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) || !pt1 || !pt2 )
6250 {
6251 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6252 return QVariant();
6253 }
6254
6255 return pt1->inclination( *pt2 );
6256}
6257
6258static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6259{
6260 if ( values.length() != 3 )
6261 return QVariant();
6262
6263 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6264 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6265 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6266
6267 QgsGeometry geom = fGeom.extrude( x, y );
6268
6269 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6270 return result;
6271}
6272
6273static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6274{
6275 if ( values.length() < 2 )
6276 return QVariant();
6277
6278 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6279
6280 if ( !fGeom.isMultipart() )
6281 return values.at( 0 );
6282
6283 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6284 QVariant cachedExpression;
6285 if ( ctx )
6286 cachedExpression = ctx->cachedValue( expString );
6287 QgsExpression expression;
6288
6289 if ( cachedExpression.isValid() )
6290 {
6291 expression = cachedExpression.value<QgsExpression>();
6292 }
6293 else
6294 expression = QgsExpression( expString );
6295
6296 bool asc = values.value( 2 ).toBool();
6297
6298 QgsExpressionContext *unconstedContext = nullptr;
6299 QgsFeature f;
6300 if ( ctx )
6301 {
6302 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6303 // so no reason to worry
6304 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6305 f = ctx->feature();
6306 }
6307 else
6308 {
6309 // If there's no context provided, create a fake one
6310 unconstedContext = new QgsExpressionContext();
6311 }
6312
6314 Q_ASSERT( collection ); // Should have failed the multipart check above
6315
6317 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6318 QgsExpressionSorter sorter( orderBy );
6319
6320 QList<QgsFeature> partFeatures;
6321 partFeatures.reserve( collection->partCount() );
6322 for ( int i = 0; i < collection->partCount(); ++i )
6323 {
6324 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6325 partFeatures << f;
6326 }
6327
6328 sorter.sortFeatures( partFeatures, unconstedContext );
6329
6331
6332 Q_ASSERT( orderedGeom );
6333
6334 while ( orderedGeom->partCount() )
6335 orderedGeom->removeGeometry( 0 );
6336
6337 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6338 {
6339 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6340 }
6341
6342 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6343
6344 if ( !ctx )
6345 delete unconstedContext;
6346
6347 return result;
6348}
6349
6350static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6351{
6352 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6353 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6354
6355 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6356
6357 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6358 return result;
6359}
6360
6361static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6362{
6363 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6364 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6365
6366 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6367
6368 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6369 return result;
6370}
6371
6372static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6373{
6374 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6375 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6376
6377 QgsGeometry geom = lineGeom.interpolate( distance );
6378
6379 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6380 return result;
6381}
6382
6383static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6384{
6385 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6386 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6387 const bool use3DDistance = values.at( 2 ).toBool();
6388
6389 double x, y, z, distance;
6390
6392 if ( !line )
6393 {
6394 return QVariant();
6395 }
6396
6397 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6398 {
6399 QgsPoint point( x, y );
6400 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6401 {
6402 point.addZValue( z );
6403 }
6404 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6405 }
6406
6407 return QVariant();
6408}
6409
6410static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6411{
6412 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6413 if ( lineGeom.type() != Qgis::GeometryType::Line )
6414 {
6415 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6416 return QVariant();
6417 }
6418
6419 const QgsCurve *curve = nullptr;
6420 if ( !lineGeom.isMultipart() )
6421 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6422 else
6423 {
6425 {
6426 if ( collection->numGeometries() > 0 )
6427 {
6428 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6429 }
6430 }
6431 }
6432 if ( !curve )
6433 return QVariant();
6434
6435 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6436 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6437
6438 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6439 QgsGeometry result( std::move( substring ) );
6440 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6441}
6442
6443static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6444{
6445 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6446 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6447
6448 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6449}
6450
6451static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6452{
6453 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6454 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6455 if ( vertex < 0 )
6456 {
6457 //negative idx
6458 int count = geom.constGet()->nCoordinates();
6459 vertex = count + vertex;
6460 }
6461
6462 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6463}
6464
6465static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6466{
6467 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6468 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6469 if ( vertex < 0 )
6470 {
6471 //negative idx
6472 int count = geom.constGet()->nCoordinates();
6473 vertex = count + vertex;
6474 }
6475
6476 return geom.distanceToVertex( vertex );
6477}
6478
6479static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6480{
6481 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6482 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6483
6484 double distance = lineGeom.lineLocatePoint( pointGeom );
6485
6486 return distance >= 0 ? distance : QVariant();
6487}
6488
6489static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6490{
6491 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6492 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6493 const bool use3DDistance = values.at( 2 ).toBool();
6494
6495 double x, y, z, distance;
6496
6498 if ( !line )
6499 {
6500 return QVariant();
6501 }
6502
6503 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6504 return found ? distance : QVariant();
6505}
6506
6507static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6508{
6509 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6510 {
6511 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6512 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6513 }
6514
6515 if ( values.length() >= 1 )
6516 {
6517 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6518 return QVariant( qlonglong( std::round( number ) ) );
6519 }
6520
6521 return QVariant();
6522}
6523
6524static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6525{
6526 Q_UNUSED( values )
6527 Q_UNUSED( parent )
6528 return M_PI;
6529}
6530
6531static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6532{
6533 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6534 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6535 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6536 if ( places < 0 )
6537 {
6538 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6539 return QVariant();
6540 }
6541
6542 const bool omitGroupSeparator = values.value( 3 ).toBool();
6543 const bool trimTrailingZeros = values.value( 4 ).toBool();
6544
6545 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6546 if ( !omitGroupSeparator )
6547 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6548 else
6549 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6550
6551 QString res = locale.toString( value, 'f', places );
6552
6553 if ( trimTrailingZeros )
6554 {
6555 const QChar decimal = locale.decimalPoint().at( 0 );
6556 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6557
6558 if ( res.contains( decimal ) )
6559 {
6560 int trimPoint = res.length() - 1;
6561
6562 while ( res.at( trimPoint ) == zeroDigit )
6563 trimPoint--;
6564
6565 if ( res.at( trimPoint ) == decimal )
6566 trimPoint--;
6567
6568 res.truncate( trimPoint + 1 );
6569 }
6570 }
6571
6572 return res;
6573}
6574
6575static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6576{
6577 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6578 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6579 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6580
6581 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6582 if ( format.indexOf( "Z" ) > 0 )
6583 datetime = datetime.toUTC();
6584
6585 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6586 return locale.toString( datetime, format );
6587}
6588
6589static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6590{
6591 const QVariant variant = values.at( 0 );
6592 bool isQColor;
6593 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6594 if ( !color.isValid() )
6595 return QVariant();
6596
6597 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6598 if ( color.spec() == QColor::Spec::Cmyk )
6599 {
6600 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() )
6601 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6602 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6603 }
6604 else
6605 {
6606 const float avg = ( color.redF() + color.greenF() + color.blueF() )
6607 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6608 color.setRgbF( avg, avg, avg, alpha );
6609 }
6610
6611 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6612}
6613
6614static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6615{
6616 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6617 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6618 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6619 if ( ratio > 1 )
6620 {
6621 ratio = 1;
6622 }
6623 else if ( ratio < 0 )
6624 {
6625 ratio = 0;
6626 }
6627
6628 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6629 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6630 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6631 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6632
6633 QColor newColor( red, green, blue, alpha );
6634
6635 return QgsSymbolLayerUtils::encodeColor( newColor );
6636}
6637
6638static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6639{
6640 const QVariant variant1 = values.at( 0 );
6641 const QVariant variant2 = values.at( 1 );
6642
6643 if ( variant1.userType() != variant2.userType() )
6644 {
6645 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6646 return QVariant();
6647 }
6648
6649 bool isQColor;
6650 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6651 if ( !color1.isValid() )
6652 return QVariant();
6653
6654 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6655 if ( !color2.isValid() )
6656 return QVariant();
6657
6658 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6659 {
6660 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6661 return QVariant();
6662 }
6663
6664 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6665
6666 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6667 // NOLINTBEGIN(bugprone-narrowing-conversions)
6668
6669 QColor newColor;
6670 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6671 if ( color1.spec() == QColor::Spec::Cmyk )
6672 {
6673 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6674 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6675 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6676 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6677 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6678 }
6679 else
6680 {
6681 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6682 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6683 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6684 newColor = QColor::fromRgbF( red, green, blue, alpha );
6685 }
6686
6687 // NOLINTEND(bugprone-narrowing-conversions)
6688
6689 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6690}
6691
6692static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6693{
6694 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6695 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6696 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6697 QColor color = QColor( red, green, blue );
6698 if ( !color.isValid() )
6699 {
6700 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6701 color = QColor( 0, 0, 0 );
6702 }
6703
6704 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6705}
6706
6707static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6708{
6709 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6710 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6711 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6712 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6713 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6714 if ( !color.isValid() )
6715 {
6716 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6717 return QVariant();
6718 }
6719
6720 return color;
6721}
6722
6723static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6724{
6725 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6726 QVariant value = node->eval( parent, context );
6727 if ( parent->hasEvalError() )
6728 {
6729 parent->setEvalErrorString( QString() );
6730 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6732 value = node->eval( parent, context );
6734 }
6735 return value;
6736}
6737
6738static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6739{
6740 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6742 QVariant value = node->eval( parent, context );
6744 if ( value.toBool() )
6745 {
6746 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6748 value = node->eval( parent, context );
6750 }
6751 else
6752 {
6753 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6755 value = node->eval( parent, context );
6757 }
6758 return value;
6759}
6760
6761static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6762{
6763 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6764 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6765 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6766 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6767 QColor color = QColor( red, green, blue, alpha );
6768 if ( !color.isValid() )
6769 {
6770 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6771 color = QColor( 0, 0, 0 );
6772 }
6773 return QgsSymbolLayerUtils::encodeColor( color );
6774}
6775
6776QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6777{
6778 QgsGradientColorRamp expRamp;
6779 const QgsColorRamp *ramp = nullptr;
6780 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6781 {
6782 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6783 ramp = &expRamp;
6784 }
6785 else
6786 {
6787 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6788 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6789 if ( !ramp )
6791 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6792 return QVariant();
6793 }
6794 }
6795
6796 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6797 QColor color = ramp->color( value );
6798 return color;
6799}
6800
6801QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6802{
6803 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6804 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6805}
6806
6807static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6808{
6809 // Hue ranges from 0 - 360
6810 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6811 // Saturation ranges from 0 - 100
6812 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6813 // Lightness ranges from 0 - 100
6814 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6815
6816 QColor color = QColor::fromHslF( hue, saturation, lightness );
6817
6818 if ( !color.isValid() )
6819 {
6820 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6821 color = QColor( 0, 0, 0 );
6822 }
6823
6824 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6825}
6826
6827static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6828{
6829 // Hue ranges from 0 - 360
6830 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6831 // Saturation ranges from 0 - 100
6832 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6833 // Lightness ranges from 0 - 100
6834 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6835 // Alpha ranges from 0 - 255
6836 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6837
6838 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6839 if ( !color.isValid() )
6840 {
6841 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6842 color = QColor( 0, 0, 0 );
6843 }
6844 return QgsSymbolLayerUtils::encodeColor( color );
6845}
6846
6847static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6848{
6849 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6850 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6851 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6852 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6853
6854 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6855 if ( !color.isValid() )
6856 {
6857 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6858 return QVariant();
6859 }
6860
6861 return color;
6862}
6863
6864static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6865{
6866 // Hue ranges from 0 - 360
6867 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6868 // Saturation ranges from 0 - 100
6869 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6870 // Value ranges from 0 - 100
6871 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6872
6873 QColor color = QColor::fromHsvF( hue, saturation, value );
6874
6875 if ( !color.isValid() )
6876 {
6877 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6878 color = QColor( 0, 0, 0 );
6879 }
6880
6881 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6882}
6883
6884static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6885{
6886 // Hue ranges from 0 - 360
6887 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6888 // Saturation ranges from 0 - 100
6889 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6890 // Value ranges from 0 - 100
6891 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6892 // Alpha ranges from 0 - 255
6893 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6894
6895 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6896 if ( !color.isValid() )
6897 {
6898 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6899 color = QColor( 0, 0, 0 );
6900 }
6901 return QgsSymbolLayerUtils::encodeColor( color );
6902}
6903
6904static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6905{
6906 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6907 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6908 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6909 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6910 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6911
6912 if ( !color.isValid() )
6913 {
6914 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6915 return QVariant();
6916 }
6917
6918 return color;
6919}
6920
6921static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6922{
6923 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6924 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6925 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6926 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6927 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6928
6929 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6930 if ( !color.isValid() )
6931 {
6932 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6933 return QVariant();
6934 }
6935
6936 return color;
6937}
6938
6939static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6940{
6941 // Cyan ranges from 0 - 100
6942 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6943 // Magenta ranges from 0 - 100
6944 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6945 // Yellow ranges from 0 - 100
6946 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6947 // Black ranges from 0 - 100
6948 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6949
6950 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6951
6952 if ( !color.isValid() )
6953 {
6954 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6955 color = QColor( 0, 0, 0 );
6956 }
6957
6958 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6959}
6960
6961static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6962{
6963 // Cyan ranges from 0 - 100
6964 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6965 // Magenta ranges from 0 - 100
6966 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6967 // Yellow ranges from 0 - 100
6968 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6969 // Black ranges from 0 - 100
6970 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6971 // Alpha ranges from 0 - 255
6972 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6973
6974 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6975 if ( !color.isValid() )
6976 {
6977 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6978 color = QColor( 0, 0, 0 );
6979 }
6980 return QgsSymbolLayerUtils::encodeColor( color );
6981}
6982
6983static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6984{
6985 const QVariant variant = values.at( 0 );
6986 bool isQColor;
6987 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6988 if ( !color.isValid() )
6989 return QVariant();
6990
6991 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6992 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6993 return color.red();
6994 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6995 return color.green();
6996 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6997 return color.blue();
6998 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6999 return color.alpha();
7000 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7001 return static_cast< double >( color.hsvHueF() * 360 );
7002 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7003 return static_cast< double >( color.hsvSaturationF() * 100 );
7004 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7005 return static_cast< double >( color.valueF() * 100 );
7006 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7007 return static_cast< double >( color.hslHueF() * 360 );
7008 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7009 return static_cast< double >( color.hslSaturationF() * 100 );
7010 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7011 return static_cast< double >( color.lightnessF() * 100 );
7012 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7013 return static_cast< double >( color.cyanF() * 100 );
7014 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7015 return static_cast< double >( color.magentaF() * 100 );
7016 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7017 return static_cast< double >( color.yellowF() * 100 );
7018 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7019 return static_cast< double >( color.blackF() * 100 );
7020
7021 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7022 return QVariant();
7023}
7024
7025static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7026{
7027 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7028 if ( map.empty() )
7029 {
7030 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
7031 return QVariant();
7032 }
7033
7034 QList< QColor > colors;
7036 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7037 {
7038 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
7039 if ( !colors.last().isValid() )
7040 {
7041 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
7042 return QVariant();
7043 }
7044
7045 double step = it.key().toDouble();
7046 if ( it == map.constBegin() )
7047 {
7048 if ( step != 0.0 )
7049 stops << QgsGradientStop( step, colors.last() );
7050 }
7051 else if ( it == map.constEnd() )
7052 {
7053 if ( step != 1.0 )
7054 stops << QgsGradientStop( step, colors.last() );
7055 }
7056 else
7057 {
7058 stops << QgsGradientStop( step, colors.last() );
7059 }
7060 }
7061 bool discrete = values.at( 1 ).toBool();
7062
7063 if ( colors.empty() )
7064 return QVariant();
7065
7066 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
7067}
7068
7069static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7070{
7071 const QVariant variant = values.at( 0 );
7072 bool isQColor;
7073 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7074 if ( !color.isValid() )
7075 return QVariant();
7076
7077 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7078 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7079 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
7080 color.setRed( std::clamp( value, 0, 255 ) );
7081 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
7082 color.setGreen( std::clamp( value, 0, 255 ) );
7083 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
7084 color.setBlue( std::clamp( value, 0, 255 ) );
7085 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
7086 color.setAlpha( std::clamp( value, 0, 255 ) );
7087 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7088 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
7089 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7090 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
7091 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7092 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7093 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7094 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
7095 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7096 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
7097 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7098 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7099 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7100 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
7101 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7102 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
7103 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7104 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
7105 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7106 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7107 else
7108 {
7109 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7110 return QVariant();
7111 }
7112 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7113}
7114
7115static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7116{
7117 const QVariant variant = values.at( 0 );
7118 bool isQColor;
7119 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7120 if ( !color.isValid() )
7121 return QVariant();
7122
7123 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7124
7125 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7126}
7127
7128static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7129{
7130 const QVariant variant = values.at( 0 );
7131 bool isQColor;
7132 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7133 if ( !color.isValid() )
7134 return QVariant();
7135
7136 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7137
7138 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7139}
7140
7141static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7142{
7143 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7144 QgsGeometry geom = feat.geometry();
7145 if ( !geom.isNull() )
7146 return QVariant::fromValue( geom );
7147 return QVariant();
7148}
7149
7150static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7151{
7152 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7153 if ( !feat.isValid() )
7154 return QVariant();
7155 return feat.id();
7156}
7157
7158static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7159{
7160 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
7161 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
7162 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
7163
7164 if ( !sCrs.isValid() )
7165 return QVariant::fromValue( fGeom );
7166
7167 if ( !dCrs.isValid() )
7168 return QVariant::fromValue( fGeom );
7169
7171 if ( context )
7172 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
7173 QgsCoordinateTransform t( sCrs, dCrs, tContext );
7174 try
7175 {
7177 return QVariant::fromValue( fGeom );
7178 }
7179 catch ( QgsCsException &cse )
7180 {
7181 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
7182 return QVariant();
7183 }
7184 return QVariant();
7185}
7186
7187
7188static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7189{
7190 bool foundLayer = false;
7191 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7192
7193 //no layer found
7194 if ( !featureSource || !foundLayer )
7195 {
7196 return QVariant();
7197 }
7198
7199 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
7200
7202 req.setFilterFid( fid );
7203 req.setTimeout( 10000 );
7204 req.setRequestMayBeNested( true );
7205 if ( context )
7206 req.setFeedback( context->feedback() );
7207 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7208
7209 QgsFeature fet;
7210 QVariant result;
7211 if ( fIt.nextFeature( fet ) )
7212 result = QVariant::fromValue( fet );
7213
7214 return result;
7215}
7216
7217static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7218{
7219 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
7220 bool foundLayer = false;
7221 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7222
7223 //no layer found
7224 if ( !featureSource || !foundLayer )
7225 {
7226 return QVariant();
7227 }
7229 QString cacheValueKey;
7230 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7231 {
7232 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7233
7234 QMap<QString, QVariant>::const_iterator i = attributeMap.constBegin();
7235 QString filterString;
7236 for ( ; i != attributeMap.constEnd(); ++i )
7237 {
7238 if ( !filterString.isEmpty() )
7239 {
7240 filterString.append( " AND " );
7241 }
7242 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7243 }
7244 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7245 if ( context && context->hasCachedValue( cacheValueKey ) )
7246 {
7247 return context->cachedValue( cacheValueKey );
7248 }
7249 req.setFilterExpression( filterString );
7250 }
7251 else
7252 {
7253 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7254 int attributeId = featureSource->fields().lookupField( attribute );
7255 if ( attributeId == -1 )
7256 {
7257 return QVariant();
7258 }
7259
7260 const QVariant &attVal = values.at( 2 );
7261
7262 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7263 if ( context && context->hasCachedValue( cacheValueKey ) )
7264 {
7265 return context->cachedValue( cacheValueKey );
7266 }
7267
7269 }
7270 req.setLimit( 1 );
7271 req.setTimeout( 10000 );
7272 req.setRequestMayBeNested( true );
7273 if ( context )
7274 req.setFeedback( context->feedback() );
7275 if ( !parent->needsGeometry() )
7276 {
7278 }
7279 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7280
7281 QgsFeature fet;
7282 QVariant res;
7283 if ( fIt.nextFeature( fet ) )
7284 {
7285 res = QVariant::fromValue( fet );
7286 }
7287
7288 if ( context )
7289 context->setCachedValue( cacheValueKey, res );
7290 return res;
7291}
7292
7293static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7294{
7295 QVariant result;
7296 QString fieldName;
7297
7298 if ( context )
7299 {
7300 if ( !values.isEmpty() )
7301 {
7302 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7303 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7304 fieldName = col->name();
7305 else if ( values.size() == 2 )
7306 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7307 }
7308
7309 QVariant value = values.at( 0 );
7310
7311 const QgsFields fields = context->fields();
7312 int fieldIndex = fields.lookupField( fieldName );
7313
7314 if ( fieldIndex == -1 )
7315 {
7316 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7317 }
7318 else
7319 {
7320 // TODO this function is NOT thread safe
7322 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7324
7325 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7326 if ( context->hasCachedValue( cacheValueKey ) )
7327 {
7328 return context->cachedValue( cacheValueKey );
7329 }
7330
7331 if ( layer )
7332 {
7333 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7335
7336 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7337
7338 QVariant cache;
7339 if ( !context->hasCachedValue( cacheKey ) )
7340 {
7341 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7342 context->setCachedValue( cacheKey, cache );
7343 }
7344 else
7345 cache = context->cachedValue( cacheKey );
7346
7347 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7348 }
7349
7350 context->setCachedValue( cacheValueKey, result );
7351 }
7352 }
7353 else
7354 {
7355 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7356 }
7357
7358 return result;
7359}
7360
7361static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7362{
7363 const QVariant data = values.at( 0 );
7364 const QMimeDatabase db;
7365 return db.mimeTypeForData( data.toByteArray() ).name();
7366}
7367
7368static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7369{
7370 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7371
7372 bool translate = true;
7373 if ( values.length() >= 3 )
7374 {
7375 translate = QgsExpressionUtils::getTVLValue( values.at( 2 ), parent ) == QgsExpressionUtils::TVL::True;
7376 }
7377
7378 bool foundLayer = false;
7379 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7380 values.at( 0 ),
7381 context,
7382 parent,
7383 [layerProperty, translate]( QgsMapLayer *layer ) -> QVariant {
7384 if ( !layer )
7385 return QVariant();
7386
7387 // here, we always prefer the layer metadata values over the older server-specific published values
7388 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7389 return layer->name();
7390 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7391 return layer->id();
7392 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7393 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7394 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7395 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7396 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7397 {
7398 QStringList keywords;
7399 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7400 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7401 {
7402 keywords.append( it.value() );
7403 }
7404 if ( !keywords.isEmpty() )
7405 return keywords;
7406 return layer->serverProperties()->keywordList();
7407 }
7408 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7409 return layer->serverProperties()->dataUrl();
7410 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7411 {
7412 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7413 }
7414 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7415 return layer->serverProperties()->attributionUrl();
7416 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7417 return layer->publicSource();
7418 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7419 return layer->minimumScale();
7420 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7421 return layer->maximumScale();
7422 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7423 return layer->isEditable();
7424 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7425 return layer->crs().authid();
7426 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7427 return layer->crs().toProj();
7428 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7429 return layer->crs().description();
7430 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7431 return layer->crs().ellipsoidAcronym();
7432 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7433 {
7434 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7435 QVariant result = QVariant::fromValue( extentGeom );
7436 return result;
7437 }
7438 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7439 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7440 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7441 {
7442 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7443 return decodedUri.value( u"path"_s );
7444 }
7445 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7446 {
7447 switch ( layer->type() )
7448 {
7450 return translate ? QCoreApplication::translate( "expressions", "Vector" ) : u"Vector"_s;
7452 return translate ? QCoreApplication::translate( "expressions", "Raster" ) : u"Raster"_s;
7454 return translate ? QCoreApplication::translate( "expressions", "Mesh" ) : u"Mesh"_s;
7456 return translate ? QCoreApplication::translate( "expressions", "Vector Tile" ) : u"Vector Tile"_s;
7458 return translate ? QCoreApplication::translate( "expressions", "Plugin" ) : u"Plugin"_s;
7460 return translate ? QCoreApplication::translate( "expressions", "Annotation" ) : u"Annotation"_s;
7462 return translate ? QCoreApplication::translate( "expressions", "Point Cloud" ) : u"Point Cloud"_s;
7464 return translate ? QCoreApplication::translate( "expressions", "Group" ) : u"Group"_s;
7466 return translate ? QCoreApplication::translate( "expressions", "Tiled Scene" ) : u"Tiled Scene"_s;
7467 }
7468 }
7469 else
7470 {
7471 //vector layer methods
7472 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7473 if ( vLayer )
7474 {
7475 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7476 return vLayer->storageType();
7477 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7479 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7480 return QVariant::fromValue( vLayer->featureCount() );
7481 }
7482 }
7483
7484 return QVariant();
7485 },
7486 foundLayer
7487 );
7488
7489 if ( !foundLayer )
7490 return QVariant();
7491 else
7492 return res;
7493}
7494
7495static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7496{
7497 const QString uriPart = values.at( 1 ).toString();
7498
7499 bool foundLayer = false;
7500
7501 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7502 values.at( 0 ),
7503 context,
7504 parent,
7505 [parent, uriPart]( QgsMapLayer *layer ) -> QVariant {
7506 if ( !layer->dataProvider() )
7507 {
7508 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7509 return QVariant();
7510 }
7511
7512 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7513
7514 if ( !uriPart.isNull() )
7515 {
7516 return decodedUri.value( uriPart );
7517 }
7518 else
7519 {
7520 return decodedUri;
7521 }
7522 },
7523 foundLayer
7524 );
7525
7526 if ( !foundLayer )
7527 {
7528 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7529 return QVariant();
7530 }
7531 else
7532 {
7533 return res;
7534 }
7535}
7536
7537static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7538{
7539 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7540 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7541
7542 bool foundLayer = false;
7543 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7544 values.at( 0 ),
7545 context,
7546 parent,
7547 [parent, band, layerProperty]( QgsMapLayer *layer ) -> QVariant {
7548 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7549 if ( !rl )
7550 return QVariant();
7551
7552 if ( band < 1 || band > rl->bandCount() )
7553 {
7554 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7555 return QVariant();
7556 }
7557
7559
7560 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7562 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7564 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7566 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7568 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7570 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7572 else
7573 {
7574 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7575 return QVariant();
7576 }
7577
7578 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7579 switch ( stat )
7580 {
7582 return stats.mean;
7584 return stats.stdDev;
7586 return stats.minimumValue;
7588 return stats.maximumValue;
7590 return stats.range;
7592 return stats.sum;
7593 default:
7594 break;
7595 }
7596 return QVariant();
7597 },
7598 foundLayer
7599 );
7600
7601 if ( !foundLayer )
7602 {
7603#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7604 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7605#endif
7606 return QVariant();
7607 }
7608 else
7609 {
7610 return res;
7611 }
7612}
7613
7614static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7615{
7616 return values;
7617}
7618
7619static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7620{
7621 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7622 bool ascending = values.value( 1 ).toBool();
7623 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7624 return list;
7625}
7626
7627static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7628{
7629 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7630}
7631
7632static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7633{
7634 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7635}
7636
7637static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7638{
7639 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7640}
7641
7642static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7643{
7644 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7645 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7646 int match = 0;
7647 for ( const auto &item : listB )
7648 {
7649 if ( listA.contains( item ) )
7650 match++;
7651 }
7652
7653 return QVariant( match == listB.count() );
7654}
7655
7656static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7657{
7658 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7659}
7660
7661static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7662{
7663 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7664 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7665 if ( pos < list.length() && pos >= 0 )
7666 return list.at( pos );
7667 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7668 return list.at( list.length() + pos );
7669 return QVariant();
7670}
7671
7672static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7673{
7674 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7675 return list.value( 0 );
7676}
7677
7678static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7679{
7680 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7681 return list.value( list.size() - 1 );
7682}
7683
7684static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7685{
7686 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7687 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7688}
7689
7690static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7691{
7692 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7693 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7694}
7695
7696static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7697{
7698 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7699 int i = 0;
7700 double total = 0.0;
7701 for ( const QVariant &item : list )
7702 {
7703 switch ( item.userType() )
7704 {
7705 case QMetaType::Int:
7706 case QMetaType::UInt:
7707 case QMetaType::LongLong:
7708 case QMetaType::ULongLong:
7709 case QMetaType::Float:
7710 case QMetaType::Double:
7711 total += item.toDouble();
7712 ++i;
7713 break;
7714 }
7715 }
7716 return i == 0 ? QVariant() : total / i;
7717}
7718
7719static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7720{
7721 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7722 QVariantList numbers;
7723 for ( const auto &item : list )
7724 {
7725 switch ( item.userType() )
7726 {
7727 case QMetaType::Int:
7728 case QMetaType::UInt:
7729 case QMetaType::LongLong:
7730 case QMetaType::ULongLong:
7731 case QMetaType::Float:
7732 case QMetaType::Double:
7733 numbers.append( item );
7734 break;
7735 }
7736 }
7737 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7738 const int count = numbers.count();
7739 if ( count == 0 )
7740 {
7741 return QVariant();
7742 }
7743 else if ( count % 2 )
7744 {
7745 return numbers.at( count / 2 );
7746 }
7747 else
7748 {
7749 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7750 }
7751}
7752
7753static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7754{
7755 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7756 int i = 0;
7757 double total = 0.0;
7758 for ( const QVariant &item : list )
7759 {
7760 switch ( item.userType() )
7761 {
7762 case QMetaType::Int:
7763 case QMetaType::UInt:
7764 case QMetaType::LongLong:
7765 case QMetaType::ULongLong:
7766 case QMetaType::Float:
7767 case QMetaType::Double:
7768 total += item.toDouble();
7769 ++i;
7770 break;
7771 }
7772 }
7773 return i == 0 ? QVariant() : total;
7774}
7775
7776static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7777{
7778 QVariant result = value;
7779 ( void ) result.convert( static_cast<int>( type ) );
7780 return result;
7781}
7782
7783static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7784{
7785 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7786 QHash< QVariant, int > hash;
7787 for ( const auto &item : list )
7788 {
7789 ++hash[item];
7790 }
7791 const QList< int > occurrences = hash.values();
7792 if ( occurrences.empty() )
7793 return QVariantList();
7794
7795 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7796
7797 const QString option = values.at( 1 ).toString();
7798 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7799 {
7800 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7801 }
7802 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7803 {
7804 if ( hash.isEmpty() )
7805 return QVariant();
7806
7807 return QVariant( hash.key( maxValue ) );
7808 }
7809 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7810 {
7811 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7812 }
7813 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7814 {
7815 if ( maxValue * 2 <= list.size() )
7816 return QVariant();
7817
7818 return QVariant( hash.key( maxValue ) );
7819 }
7820 else
7821 {
7822 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7823 return QVariant();
7824 }
7825}
7826
7827static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7828{
7829 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7830 QHash< QVariant, int > hash;
7831 for ( const auto &item : list )
7832 {
7833 ++hash[item];
7834 }
7835 const QList< int > occurrences = hash.values();
7836 if ( occurrences.empty() )
7837 return QVariantList();
7838
7839 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7840
7841 const QString option = values.at( 1 ).toString();
7842 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7843 {
7844 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7845 }
7846 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7847 {
7848 if ( hash.isEmpty() )
7849 return QVariant();
7850
7851 return QVariant( hash.key( minValue ) );
7852 }
7853 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7854 {
7855 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7856 }
7857 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7858 {
7859 if ( hash.isEmpty() )
7860 return QVariant();
7861
7862 // Remove the majority, all others are minority
7863 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7864 if ( maxValue * 2 > list.size() )
7865 hash.remove( hash.key( maxValue ) );
7866
7867 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7868 }
7869 else
7870 {
7871 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7872 return QVariant();
7873 }
7874}
7875
7876static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7877{
7878 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7879 list.append( values.at( 1 ) );
7880 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7881}
7882
7883static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7884{
7885 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7886 list.prepend( values.at( 1 ) );
7887 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7888}
7889
7890static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7891{
7892 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7893 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7894 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7895}
7896
7897static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7898{
7899 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7900 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7901 if ( position < 0 )
7902 position = position + list.length();
7903 if ( position >= 0 && position < list.length() )
7904 list.removeAt( position );
7905 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7906}
7907
7908static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7909{
7910 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7911 return QVariant();
7912
7913 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7914
7915 const QVariant toRemove = values.at( 1 );
7916 if ( QgsVariantUtils::isNull( toRemove ) )
7917 {
7918 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant &element ) { return QgsVariantUtils::isNull( element ); } ), list.end() );
7919 }
7920 else
7921 {
7922 list.removeAll( toRemove );
7923 }
7924 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7925}
7926
7927static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7928{
7929 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7930 {
7931 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7932
7933 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7934 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7935 {
7936 int index = list.indexOf( it.key() );
7937 while ( index >= 0 )
7938 {
7939 list.replace( index, it.value() );
7940 index = list.indexOf( it.key() );
7941 }
7942 }
7943
7944 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7945 }
7946 else if ( values.count() == 3 )
7947 {
7948 QVariantList before;
7949 QVariantList after;
7950 bool isSingleReplacement = false;
7951
7952 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7953 {
7954 before = QVariantList() << values.at( 1 );
7955 }
7956 else
7957 {
7958 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7959 }
7960
7961 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7962 {
7963 after = QVariantList() << values.at( 2 );
7964 isSingleReplacement = true;
7965 }
7966 else
7967 {
7968 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7969 }
7970
7971 if ( !isSingleReplacement && before.length() != after.length() )
7972 {
7973 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7974 return QVariant();
7975 }
7976
7977 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7978 for ( int i = 0; i < before.length(); i++ )
7979 {
7980 int index = list.indexOf( before.at( i ) );
7981 while ( index >= 0 )
7982 {
7983 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7984 index = list.indexOf( before.at( i ) );
7985 }
7986 }
7987
7988 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7989 }
7990 else
7991 {
7992 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7993 return QVariant();
7994 }
7995}
7996
7997static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7998{
7999 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8000 QVariantList list_new;
8001
8002 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
8003 {
8004 while ( list.removeOne( cur ) )
8005 {
8006 list_new.append( cur );
8007 }
8008 }
8009
8010 list_new.append( list );
8011
8012 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
8013}
8014
8015static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8016{
8017 QVariantList list;
8018 for ( const QVariant &cur : values )
8019 {
8020 list += QgsExpressionUtils::getListValue( cur, parent );
8021 }
8022 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
8023}
8024
8025static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8026{
8027 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8028 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
8029 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
8030 int slice_length = 0;
8031 // negative positions means positions taken relative to the end of the array
8032 if ( start_pos < 0 )
8033 {
8034 start_pos = list.length() + start_pos;
8035 }
8036 if ( end_pos >= 0 )
8037 {
8038 slice_length = end_pos - start_pos + 1;
8039 }
8040 else
8041 {
8042 slice_length = list.length() + end_pos - start_pos + 1;
8043 }
8044 //avoid negative lengths in QList.mid function
8045 if ( slice_length < 0 )
8046 {
8047 slice_length = 0;
8048 }
8049 list = list.mid( start_pos, slice_length );
8050 return list;
8051}
8052
8053static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8054{
8055 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8056 std::reverse( list.begin(), list.end() );
8057 return list;
8058}
8059
8060static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8061{
8062 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8063 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
8064 for ( const QVariant &cur : array2 )
8065 {
8066 if ( array1.contains( cur ) )
8067 return QVariant( true );
8068 }
8069 return QVariant( false );
8070}
8071
8072static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8073{
8074 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8075
8076 QVariantList distinct;
8077
8078 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8079 {
8080 if ( !distinct.contains( *it ) )
8081 {
8082 distinct += ( *it );
8083 }
8084 }
8085
8086 return distinct;
8087}
8088
8089static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8090{
8091 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8092 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8093 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8094
8095 QString str;
8096
8097 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8098 {
8099 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
8100 if ( it != ( array.constEnd() - 1 ) )
8101 {
8102 str += delimiter;
8103 }
8104 }
8105
8106 return QVariant( str );
8107}
8108
8109static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8110{
8111 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8112 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8113 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8114
8115 QStringList list = str.split( delimiter );
8116 QVariantList array;
8117
8118 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
8119 {
8120 array += ( !( *it ).isEmpty() ) ? *it : empty;
8121 }
8122
8123 return array;
8124}
8125
8126static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8127{
8128 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8129 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
8130 if ( document.isNull() )
8131 return QVariant();
8132
8133 return document.toVariant();
8134}
8135
8136static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8137{
8138 Q_UNUSED( parent )
8139 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
8140 return QString( document.toJson( QJsonDocument::Compact ) );
8141}
8142
8143static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8144{
8145 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8146 if ( str.isEmpty() )
8147 return QVariantMap();
8148 str = str.trimmed();
8149
8150 return QgsHstoreUtils::parse( str );
8151}
8152
8153static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8154{
8155 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8156 return QgsHstoreUtils::build( map );
8157}
8158
8159static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8160{
8161 QVariantMap result;
8162 for ( int i = 0; i + 1 < values.length(); i += 2 )
8163 {
8164 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
8165 }
8166 return result;
8167}
8168
8169static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8170{
8171 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8172 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8173 QVariantMap resultMap;
8174
8175 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8176 {
8177 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
8178 }
8179
8180 return resultMap;
8181}
8182
8183static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8184{
8185 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
8186}
8187
8188static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8189{
8190 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
8191}
8192
8193static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8194{
8195 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8196 map.remove( values.at( 1 ).toString() );
8197 return map;
8198}
8199
8200static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8201{
8202 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8203 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
8204 return map;
8205}
8206
8207static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8208{
8209 QVariantMap result;
8210 for ( const QVariant &cur : values )
8211 {
8212 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
8213 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
8214 result.insert( it.key(), it.value() );
8215 }
8216 return result;
8217}
8218
8219static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8220{
8221 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
8222}
8223
8224static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8225{
8226 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
8227}
8228
8229static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8230{
8231 const QString envVarName = values.at( 0 ).toString();
8232 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
8233 return QVariant();
8234
8235 return QProcessEnvironment::systemEnvironment().value( envVarName );
8236}
8237
8238static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8239{
8240 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8241 if ( parent->hasEvalError() )
8242 {
8243 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "base_file_name"_L1 ) );
8244 return QVariant();
8245 }
8246 return QFileInfo( file ).completeBaseName();
8247}
8248
8249static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8250{
8251 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8252 if ( parent->hasEvalError() )
8253 {
8254 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_suffix"_L1 ) );
8255 return QVariant();
8256 }
8257 return QFileInfo( file ).completeSuffix();
8258}
8259
8260static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8261{
8262 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8263 if ( parent->hasEvalError() )
8264 {
8265 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8266 return QVariant();
8267 }
8268 return QFileInfo::exists( file );
8269}
8270
8271static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8272{
8273 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8274 if ( parent->hasEvalError() )
8275 {
8276 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_name"_L1 ) );
8277 return QVariant();
8278 }
8279 return QFileInfo( file ).fileName();
8280}
8281
8282static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8283{
8284 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8285 if ( parent->hasEvalError() )
8286 {
8287 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_file"_L1 ) );
8288 return QVariant();
8289 }
8290 return QFileInfo( file ).isFile();
8291}
8292
8293static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8294{
8295 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8296 if ( parent->hasEvalError() )
8297 {
8298 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8299 return QVariant();
8300 }
8301 return QFileInfo( file ).isDir();
8302}
8303
8304static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8305{
8306 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8307 if ( parent->hasEvalError() )
8308 {
8309 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8310 return QVariant();
8311 }
8312 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8313}
8314
8315static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8316{
8317 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8318 if ( parent->hasEvalError() )
8319 {
8320 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8321 return QVariant();
8322 }
8323 return QFileInfo( file ).size();
8324}
8325
8326static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8327{
8328 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8329}
8330
8331static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8332{
8333 QVariant hash;
8334 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8335 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8336
8337 if ( method == "md4"_L1 )
8338 {
8339 hash = fcnHash( str, QCryptographicHash::Md4 );
8340 }
8341 else if ( method == "md5"_L1 )
8342 {
8343 hash = fcnHash( str, QCryptographicHash::Md5 );
8344 }
8345 else if ( method == "sha1"_L1 )
8346 {
8347 hash = fcnHash( str, QCryptographicHash::Sha1 );
8348 }
8349 else if ( method == "sha224"_L1 )
8350 {
8351 hash = fcnHash( str, QCryptographicHash::Sha224 );
8352 }
8353 else if ( method == "sha256"_L1 )
8354 {
8355 hash = fcnHash( str, QCryptographicHash::Sha256 );
8356 }
8357 else if ( method == "sha384"_L1 )
8358 {
8359 hash = fcnHash( str, QCryptographicHash::Sha384 );
8360 }
8361 else if ( method == "sha512"_L1 )
8362 {
8363 hash = fcnHash( str, QCryptographicHash::Sha512 );
8364 }
8365 else if ( method == "sha3_224"_L1 )
8366 {
8367 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8368 }
8369 else if ( method == "sha3_256"_L1 )
8370 {
8371 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8372 }
8373 else if ( method == "sha3_384"_L1 )
8374 {
8375 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8376 }
8377 else if ( method == "sha3_512"_L1 )
8378 {
8379 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8380 }
8381 else if ( method == "keccak_224"_L1 )
8382 {
8383 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8384 }
8385 else if ( method == "keccak_256"_L1 )
8386 {
8387 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8388 }
8389 else if ( method == "keccak_384"_L1 )
8390 {
8391 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8392 }
8393 else if ( method == "keccak_512"_L1 )
8394 {
8395 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8396 }
8397 else
8398 {
8399 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8400 }
8401 return hash;
8402}
8403
8404static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8405{
8406 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8407}
8408
8409static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8410{
8411 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8412}
8413
8414static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8415{
8416 const QByteArray input = values.at( 0 ).toByteArray();
8417 return QVariant( QString( input.toBase64() ) );
8418}
8419
8420static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8421{
8422 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8423 QUrlQuery query;
8424 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8425 {
8426 query.addQueryItem( it.key(), it.value().toString() );
8427 }
8428 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8430
8431static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8432{
8433 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8434 const QByteArray base64 = value.toLocal8Bit();
8435 const QByteArray decoded = QByteArray::fromBase64( base64 );
8436 return QVariant( decoded );
8437}
8438
8440typedef std::function<bool( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend )> RelationFunction;
8441
8442static QVariant executeGeomOverlay(
8443 const QVariantList &values,
8444 const QgsExpressionContext *context,
8445 QgsExpression *parent,
8446 const RelationFunction &relationFunction,
8447 bool invert = false,
8448 double bboxGrow = 0,
8449 bool isNearestFunc = false,
8450 bool isIntersectsFunc = false
8451)
8452{
8453 if ( !context )
8454 {
8455 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8456 return QVariant();
8457 }
8458
8459 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8460 // TODO this function is NOT thread safe
8462 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8464
8465 QgsFeatureRequest request;
8466 request.setTimeout( 10000 );
8467 request.setRequestMayBeNested( true );
8468 request.setFeedback( context->feedback() );
8469
8470 // First parameter is the overlay layer
8471 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8473
8474 const bool layerCanBeCached = node->isStatic( parent, context );
8475 QVariant targetLayerValue = node->eval( parent, context );
8477
8478 // Second parameter is the expression to evaluate (or null for testonly)
8479 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8481 QString subExpString = node->dump();
8482
8483 bool testOnly = ( subExpString == "NULL" );
8484 // TODO this function is NOT thread safe
8486 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8488 if ( !targetLayer ) // No layer, no joy
8489 {
8490 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8491 return QVariant();
8492 }
8493
8494 // Third parameter is the filtering expression
8495 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8497 QString filterString = node->dump();
8498 if ( filterString != "NULL" )
8499 {
8500 request.setFilterExpression( filterString ); //filter cached features
8501 }
8502
8503 // Fourth parameter is the limit
8504 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8506 QVariant limitValue = node->eval( parent, context );
8508 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8509
8510 double max_distance = 0;
8511 bool cacheEnabled = false;
8512
8513 double minOverlap { -1 };
8514 double minInscribedCircleRadius { -1 };
8515 bool returnDetails = false; //#spellok
8516 bool sortByMeasure = false;
8517 bool sortAscending = false;
8518 bool requireMeasures = false;
8519 bool overlapOrRadiusFilter = false;
8520
8522
8523 if ( isNearestFunc ) //maxdistance param handling
8524 {
8525 // Fifth parameter (for nearest only) is the max distance
8526 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8528 QVariant distanceValue = node->eval( parent, context );
8530 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8531
8532 // Sixth (for nearest only) parameter is the cache toggle
8533 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
8535 QVariant cacheValue = node->eval( parent, context );
8537 cacheEnabled = cacheValue.toBool();
8538 }
8539 else
8540 {
8541 // Fifth parameter is the cache toggle
8542 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8544 QVariant cacheValue = node->eval( parent, context );
8546 cacheEnabled = cacheValue.toBool();
8547
8548 // Sixth parameter is the min overlap (area or length)
8549 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8551 const QVariant minOverlapValue = node->eval( parent, context );
8553 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8554
8555 // Seventh parameter is the min inscribed circle radius
8556 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8558 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8560 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8561
8562 // Eighth parameter is the return_details
8563 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8564 // Return measures is only effective when an expression is set
8565 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8566
8567 // Ninth parameter is the sort_by_intersection_size flag
8568 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8569 // Sort by measures is only effective when an expression is set
8570 const QString sorting { node->eval( parent, context ).toString().toLower() };
8571 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8572 sortAscending = sorting.startsWith( "asc" );
8573 requireMeasures = sortByMeasure || returnDetails; //#spellok
8574 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8575
8576 // Tenth parameter is the geometry backend
8577 node = QgsExpressionUtils::getNode( values.at( 9 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8579 const QString backendStr = node->eval( parent, context ).toString().toUpper();
8581
8582 bool ok;
8583 backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
8584 if ( !ok )
8585 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
8586 }
8587
8588 FEAT_FROM_CONTEXT( context, feat )
8589 const QgsGeometry geometry = feat.geometry();
8590
8591 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8592 {
8593 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8594 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8595 }
8596
8597 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8598
8599 QgsRectangle intDomain = geometry.boundingBox();
8600 if ( bboxGrow != 0 )
8601 {
8602 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8603 }
8604
8605 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8606
8607 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8608 // Otherwise, it can be toggled by the user
8609 QgsSpatialIndex spatialIndex;
8610 QgsVectorLayer *cachedTarget;
8611 QList<QgsFeature> features;
8612 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8613 {
8614 // If the cache (local spatial index) is enabled, we materialize the whole
8615 // layer, then do the request on that layer instead.
8616 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8617 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8618
8619 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8620 {
8621 cachedTarget = targetLayer->materialize( request );
8622 if ( layerCanBeCached )
8623 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8624 }
8625 else
8626 {
8627 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8628 }
8629
8630 if ( !context->hasCachedValue( cacheIndex ) )
8631 {
8632 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8633 if ( layerCanBeCached )
8634 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8635 }
8636 else
8637 {
8638 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8639 }
8640
8641 QList<QgsFeatureId> fidsList;
8642 if ( isNearestFunc )
8643 {
8644 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8645 }
8646 else
8647 {
8648 fidsList = spatialIndex.intersects( intDomain );
8649 }
8650
8651 QListIterator<QgsFeatureId> i( fidsList );
8652 while ( i.hasNext() )
8653 {
8654 QgsFeatureId fId2 = i.next();
8655 if ( sameLayers && feat.id() == fId2 )
8656 continue;
8657 features.append( cachedTarget->getFeature( fId2 ) );
8658 }
8659 }
8660 else
8661 {
8662 // If the cache (local spatial index) is not enabled, we directly
8663 // get the features from the target layer
8664 request.setFilterRect( intDomain );
8665 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8666 QgsFeature feat2;
8667 while ( fit.nextFeature( feat2 ) )
8668 {
8669 if ( sameLayers && feat.id() == feat2.id() )
8670 continue;
8671 features.append( feat2 );
8672 }
8673 }
8674
8675 QgsExpression subExpression;
8676 QgsExpressionContext subContext;
8677 if ( !testOnly )
8678 {
8679 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8680 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8681
8682 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8683 {
8684 subExpression = QgsExpression( subExpString );
8686 subExpression.prepare( &subContext );
8687 }
8688 else
8689 {
8690 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8691 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8692 }
8693 }
8694
8695 // //////////////////////////////////////////////////////////////////
8696 // Helper functions for geometry tests
8697
8698 // Test function for linestring geometries, returns TRUE if test passes
8699 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double &overlapValue ) -> bool {
8700 bool testResult { false };
8701 // For return measures:
8702 QVector<double> overlapValues;
8703 const QgsGeometry merged { intersection.mergeLines() };
8704 for ( auto it = merged.const_parts_begin(); !testResult && it != merged.const_parts_end(); ++it )
8705 {
8707 // Check min overlap for intersection (if set)
8708 if ( minOverlap != -1 || requireMeasures )
8709 {
8710 overlapValue = geom->length();
8711 overlapValues.append( overlapValue );
8712 if ( minOverlap != -1 )
8713 {
8714 if ( overlapValue >= minOverlap )
8715 {
8716 testResult = true;
8717 }
8718 else
8719 {
8720 continue;
8721 }
8722 }
8723 }
8724 }
8725
8726 if ( !overlapValues.isEmpty() )
8727 {
8728 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8729 }
8730
8731 return testResult;
8732 };
8733
8734 // Test function for polygon geometries, returns TRUE if test passes
8735 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double &radiusValue, double &overlapValue ) -> bool {
8736 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8737 bool testResult { false };
8738 // For return measures:
8739 QVector<double> overlapValues;
8740 QVector<double> radiusValues;
8741 for ( auto it = intersection.const_parts_begin(); ( !testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8742 {
8744 // Check min overlap for intersection (if set)
8745 if ( minOverlap != -1 || requireMeasures )
8746 {
8747 overlapValue = geom->area();
8748 overlapValues.append( geom->area() );
8749 if ( minOverlap != -1 )
8750 {
8751 if ( overlapValue >= minOverlap )
8752 {
8753 testResult = true;
8754 }
8755 else
8756 {
8757 continue;
8758 }
8759 }
8760 }
8761
8762 // Check min inscribed circle radius for intersection (if set)
8763 if ( minInscribedCircleRadius != -1 || requireMeasures )
8764 {
8765 const QgsRectangle bbox = geom->boundingBox();
8766 const double width = bbox.width();
8767 const double height = bbox.height();
8768 const double size = width > height ? width : height;
8769 const double tolerance = size / 100.0;
8770 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8771 testResult = radiusValue >= minInscribedCircleRadius;
8772 radiusValues.append( radiusValues );
8773 }
8774 } // end for parts
8775
8776 // Get the max values
8777 if ( !radiusValues.isEmpty() )
8778 {
8779 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8780 }
8781
8782 if ( !overlapValues.isEmpty() )
8783 {
8784 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8785 }
8786
8787 return testResult;
8788 };
8789
8790
8791 bool found = false;
8792 int foundCount = 0;
8793 QVariantList results;
8794
8795 QListIterator<QgsFeature> i( features );
8796 try
8797 {
8798 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8799 {
8800 QgsFeature feat2 = i.next();
8801
8802
8803 if ( relationFunction( geometry, feat2.geometry(), values, backend ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8804 {
8805 double overlapValue = -1;
8806 double radiusValue = -1;
8807
8808 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8809 {
8810 QgsGeometry intersection { geometry.intersection( feat2.geometry(), QgsGeometryParameters() ) };
8811
8812 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8813 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8814 {
8815 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8816 intersection = QgsGeometry();
8817 QgsMultiPolygonXY poly;
8818 QgsMultiPolylineXY line;
8819 QgsMultiPointXY point;
8820 for ( const auto &geom : std::as_const( geometries ) )
8821 {
8822 switch ( geom.type() )
8823 {
8825 {
8826 poly.append( geom.asPolygon() );
8827 break;
8828 }
8830 {
8831 line.append( geom.asPolyline() );
8832 break;
8833 }
8835 {
8836 point.append( geom.asPoint() );
8837 break;
8838 }
8841 {
8842 break;
8843 }
8844 }
8845 }
8846
8847 switch ( geometry.type() )
8848 {
8850 {
8851 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8852 break;
8853 }
8855 {
8856 intersection = QgsGeometry::fromMultiPolylineXY( line );
8857 break;
8858 }
8860 {
8861 intersection = QgsGeometry::fromMultiPointXY( point );
8862 break;
8863 }
8866 {
8867 break;
8868 }
8869 }
8870 }
8871
8872 // Depending on the intersection geometry type and on the geometry type of
8873 // the tested geometry we can run different tests and collect different measures
8874 // that can be used for sorting (if required).
8875 switch ( intersection.type() )
8876 {
8878 {
8879 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8880 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8881
8882 if ( !testResult && overlapOrRadiusFilter )
8883 {
8884 continue;
8885 }
8886
8887 break;
8888 }
8889
8891 {
8892 // If the intersection is a linestring and a minimum circle is required
8893 // we can discard this result immediately.
8894 if ( minInscribedCircleRadius != -1 )
8895 {
8896 continue;
8897 }
8898
8899 // Otherwise a test for the overlap value is performed.
8900 const bool testResult { testLinestring( intersection, overlapValue ) };
8901
8902 if ( !testResult && overlapOrRadiusFilter )
8903 {
8904 continue;
8905 }
8906
8907 break;
8908 }
8909
8911 {
8912 // If the intersection is a point and a minimum circle is required
8913 // we can discard this result immediately.
8914 if ( minInscribedCircleRadius != -1 )
8915 {
8916 continue;
8917 }
8918
8919 bool testResult { false };
8920 if ( minOverlap != -1 || requireMeasures )
8921 {
8922 // Initially set this to 0 because it's a point intersection...
8923 overlapValue = 0;
8924 // ... but if the target geometry is not a point and the source
8925 // geometry is a point, we must record the length or the area
8926 // of the intersected geometry and use that as a measure for
8927 // sorting or reporting.
8928 if ( geometry.type() == Qgis::GeometryType::Point )
8929 {
8930 switch ( feat2.geometry().type() )
8931 {
8935 {
8936 break;
8937 }
8939 {
8940 testResult = testLinestring( feat2.geometry(), overlapValue );
8941 break;
8942 }
8944 {
8945 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8946 break;
8947 }
8948 }
8949 }
8950
8951 if ( !testResult && overlapOrRadiusFilter )
8952 {
8953 continue;
8954 }
8955 }
8956 break;
8957 }
8958
8961 {
8962 continue;
8963 }
8964 }
8965 }
8966
8967 found = true;
8968 foundCount++;
8969
8970 // We just want a single boolean result if there is any intersect: finish and return true
8971 if ( testOnly )
8972 break;
8973
8974 if ( !invert )
8975 {
8976 // We want a list of attributes / geometries / other expression values, evaluate now
8977 subContext.setFeature( feat2 );
8978 const QVariant expResult = subExpression.evaluate( &subContext );
8979
8980 if ( requireMeasures )
8981 {
8982 QVariantMap resultRecord;
8983 resultRecord.insert( u"id"_s, feat2.id() );
8984 resultRecord.insert( u"result"_s, expResult );
8985 // Overlap is always added because return measures was set
8986 resultRecord.insert( u"overlap"_s, overlapValue );
8987 // Radius is only added when is different than -1 (because for linestrings is not set)
8988 if ( radiusValue != -1 )
8989 {
8990 resultRecord.insert( u"radius"_s, radiusValue );
8991 }
8992 results.append( resultRecord );
8993 }
8994 else
8995 {
8996 results.append( expResult );
8997 }
8998 }
8999 else
9000 {
9001 // If not, results is a list of found ids, which we'll inverse and evaluate below
9002 results.append( feat2.id() );
9003 }
9004 }
9005 }
9006 }
9007 catch ( QgsException &e )
9008 {
9009 parent->setEvalErrorString( e.what() );
9010 return false;
9011 }
9012
9013 if ( testOnly )
9014 {
9015 if ( invert )
9016 found = !found; //for disjoint condition
9017 return found;
9018 }
9019
9020 if ( !invert )
9021 {
9022 if ( requireMeasures )
9023 {
9024 if ( sortByMeasure )
9025 {
9026 std::sort( results.begin(), results.end(), [sortAscending]( const QVariant &recordA, const QVariant &recordB ) -> bool {
9027 return sortAscending ? recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
9028 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
9029 } );
9030 }
9031 // Resize
9032 if ( limit > 0 && results.size() > limit )
9033 {
9034 results.erase( results.begin() + limit );
9035 }
9036
9037 if ( !returnDetails ) //#spellok
9038 {
9039 QVariantList expResults;
9040 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
9041 {
9042 expResults.append( it->toMap().value( u"result"_s ) );
9043 }
9044 return expResults;
9045 }
9046 }
9047
9048 return results;
9049 }
9050
9051 // for disjoint condition returns the results for cached layers not intersected feats
9052 QVariantList disjoint_results;
9053 QgsFeature feat2;
9054 QgsFeatureRequest request2;
9055 request2.setLimit( limit );
9056 if ( context )
9057 request2.setFeedback( context->feedback() );
9058 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
9059 while ( fi.nextFeature( feat2 ) )
9060 {
9061 if ( !results.contains( feat2.id() ) )
9062 {
9063 subContext.setFeature( feat2 );
9064 disjoint_results.append( subExpression.evaluate( &subContext ) );
9065 }
9066 }
9067 return disjoint_results;
9068}
9069
9070// Intersect functions:
9071
9072static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9073{
9074 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9075 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, false, true );
9076}
9077
9078static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9079{
9080 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.contains( other ); };
9081 return executeGeomOverlay( values, context, parent, geomFunction );
9082}
9083
9084static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9085{
9086 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.crosses( other ); };
9087 return executeGeomOverlay( values, context, parent, geomFunction );
9088}
9089
9090static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9091{
9092 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9093 return geometry.isExactlyEqual( other, Qgis::GeometryBackend::QGIS );
9094 };
9095 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9096}
9097
9098static QVariant fcnGeomOverlayEqualsExact( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9099{
9100 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9101 return geometry.isExactlyEqual( other, backend );
9102 };
9103 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9104}
9105
9106static QVariant fcnGeomOverlayEqualsTopological( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9107{
9108 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9109 return geometry.isTopologicallyEqual( other, backend );
9110 };
9111 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9112}
9113
9114static QVariant fcnGeomOverlayEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9115{
9116 // This parameter is the epsilon tolerance
9117 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 10 ), parent );
9119 QVariant epsilonValue = node->eval( parent, context );
9121 double epsilon = QgsExpressionUtils::getDoubleValue( epsilonValue, parent );
9122
9123 RelationFunction geomFunction = [epsilon]( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9124 return geometry.isFuzzyEqual( other, epsilon, backend );
9125 };
9126 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9127}
9128
9129static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9130{
9131 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.touches( other ); };
9132 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9133}
9134
9135static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9136{
9137 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.within( other ); };
9138 return executeGeomOverlay( values, context, parent, geomFunction );
9139}
9140
9141static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9142{
9143 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9144 return executeGeomOverlay( values, context, parent, geomFunction, true, 0, false, true );
9145}
9146
9147static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9148{
9149 RelationFunction geomFunction = []( const QgsGeometry &, const QgsGeometry &, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9150 return true; // does nothing on purpose
9151 };
9152 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, true );
9153}
9154
9156{
9157 static std::once_flag initialized;
9158 std::call_once( initialized, []() {
9160 << QgsExpressionFunction::Parameter( u"expression"_s )
9161 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
9162 << QgsExpressionFunction::Parameter( u"filter"_s, true );
9163
9164 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
9165 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true ) << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9166
9167 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
9168 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9169
9170 SharedFunctionPtrList functions;
9171 functions
9172 << std::make_shared< QgsStaticExpressionFunction >( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
9173 << std::make_shared< QgsStaticExpressionFunction >( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
9174 << std::make_shared< QgsStaticExpressionFunction >( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
9175 << std::make_shared<
9176 QgsStaticExpressionFunction >( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnAzimuth, u"GeometryGroup"_s )
9177 << std::make_shared< QgsStaticExpressionFunction >(
9178 u"bearing"_s,
9180 << QgsExpressionFunction::Parameter( u"point1"_s )
9181 << QgsExpressionFunction::Parameter( u"point2"_s )
9182 << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() )
9183 << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ),
9184 fcnBearing,
9185 u"GeometryGroup"_s
9186 )
9187 << std::make_shared<
9188 QgsStaticExpressionFunction >( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnInclination, u"GeometryGroup"_s )
9189 << std::make_shared< QgsStaticExpressionFunction >(
9190 u"project"_s,
9192 << QgsExpressionFunction::Parameter( u"point"_s )
9193 << QgsExpressionFunction::Parameter( u"distance"_s )
9194 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9195 << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ),
9196 fcnProject,
9197 u"GeometryGroup"_s
9198 )
9199 << std::make_shared< QgsStaticExpressionFunction >( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
9200 << std::make_shared< QgsStaticExpressionFunction >( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
9201 << std::make_shared< QgsStaticExpressionFunction >( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
9202 << std::make_shared< QgsStaticExpressionFunction >( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
9203 << std::make_shared< QgsStaticExpressionFunction >( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
9204 << std::make_shared< QgsStaticExpressionFunction >( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
9205 << std::make_shared< QgsStaticExpressionFunction >( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
9206 << std::make_shared<
9207 QgsStaticExpressionFunction >( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
9208 << std::make_shared< QgsStaticExpressionFunction >( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
9209 << std::make_shared< QgsStaticExpressionFunction >( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
9210 << std::make_shared< QgsStaticExpressionFunction >( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
9211 << std::make_shared<
9212 QgsStaticExpressionFunction >( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
9213 << std::make_shared<
9214 QgsStaticExpressionFunction >( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
9215
9216 auto randFunc = std::make_shared<
9217 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 );
9218 randFunc->setIsStatic( false );
9219 functions << randFunc;
9220
9221 auto randfFunc = std::make_shared< QgsStaticExpressionFunction >(
9222 u"randf"_s,
9224 << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 )
9225 << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 )
9226 << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9227 fcnRndF,
9228 u"Math"_s
9229 );
9230 randfFunc->setIsStatic( false );
9231 functions << randfFunc;
9232
9233 functions
9234 << std::make_shared< QgsStaticExpressionFunction >( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9235 << std::make_shared< QgsStaticExpressionFunction >( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9236 << std::make_shared<
9237 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 )
9238 << std::make_shared< QgsStaticExpressionFunction >(
9239 u"scale_linear"_s,
9241 << QgsExpressionFunction::Parameter( u"value"_s )
9242 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9243 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9244 << QgsExpressionFunction::Parameter( u"range_min"_s )
9245 << QgsExpressionFunction::Parameter( u"range_max"_s ),
9246 fcnLinearScale,
9247 u"Math"_s
9248 )
9249 << std::make_shared< QgsStaticExpressionFunction >(
9250 u"scale_polynomial"_s,
9252 << QgsExpressionFunction::Parameter( u"value"_s )
9253 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9254 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9255 << QgsExpressionFunction::Parameter( u"range_min"_s )
9256 << QgsExpressionFunction::Parameter( u"range_max"_s )
9257 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9258 fcnPolynomialScale,
9259 u"Math"_s,
9260 QString(),
9261 false,
9262 QSet<QString>(),
9263 false,
9264 QStringList() << u"scale_exp"_s
9265 )
9266 << std::make_shared< QgsStaticExpressionFunction >(
9267 u"scale_exponential"_s,
9269 << QgsExpressionFunction::Parameter( u"value"_s )
9270 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9271 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9272 << QgsExpressionFunction::Parameter( u"range_min"_s )
9273 << QgsExpressionFunction::Parameter( u"range_max"_s )
9274 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9275 fcnExponentialScale,
9276 u"Math"_s
9277 )
9278 << std::make_shared< QgsStaticExpressionFunction >(
9279 u"scale_cubic_bezier"_s,
9281 << QgsExpressionFunction::Parameter( u"value"_s )
9282 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9283 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9284 << QgsExpressionFunction::Parameter( u"range_min"_s )
9285 << QgsExpressionFunction::Parameter( u"range_max"_s )
9286 << QgsExpressionFunction::Parameter( u"x1"_s )
9287 << QgsExpressionFunction::Parameter( u"y1"_s )
9288 << QgsExpressionFunction::Parameter( u"x2"_s )
9289 << QgsExpressionFunction::Parameter( u"y2"_s ),
9290 fcnCubicBezierScale,
9291 u"Math"_s
9292 )
9293 << std::make_shared< QgsStaticExpressionFunction >( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
9294 << std::make_shared< QgsStaticExpressionFunction >( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
9295 << std::make_shared< QgsStaticExpressionFunction >( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
9296 << std::make_shared<
9297 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 )
9298 << std::make_shared<
9299 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 )
9300 << std::make_shared<
9301 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 )
9302 << std::make_shared< QgsStaticExpressionFunction >(
9303 u"to_string"_s,
9304 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9305 fcnToString,
9306 QStringList() << u"Conversions"_s << u"String"_s,
9307 QString(),
9308 false,
9309 QSet<QString>(),
9310 false,
9311 QStringList() << u"tostring"_s
9312 )
9313 << std::make_shared< QgsStaticExpressionFunction >(
9314 u"to_datetime"_s,
9316 << QgsExpressionFunction::Parameter( u"value"_s )
9317 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9318 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9319 fcnToDateTime,
9320 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9321 QString(),
9322 false,
9323 QSet<QString>(),
9324 false,
9325 QStringList() << u"todatetime"_s
9326 )
9327 << std::make_shared< QgsStaticExpressionFunction >(
9328 u"to_date"_s,
9330 << QgsExpressionFunction::Parameter( u"value"_s )
9331 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9332 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9333 fcnToDate,
9334 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9335 QString(),
9336 false,
9337 QSet<QString>(),
9338 false,
9339 QStringList() << u"todate"_s
9340 )
9341 << std::make_shared< QgsStaticExpressionFunction >(
9342 u"to_time"_s,
9344 << QgsExpressionFunction::Parameter( u"value"_s )
9345 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9346 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9347 fcnToTime,
9348 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9349 QString(),
9350 false,
9351 QSet<QString>(),
9352 false,
9353 QStringList() << u"totime"_s
9354 )
9355 << std::make_shared< QgsStaticExpressionFunction >(
9356 u"to_interval"_s,
9357 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9358 fcnToInterval,
9359 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9360 QString(),
9361 false,
9362 QSet<QString>(),
9363 false,
9364 QStringList() << u"tointerval"_s
9365 )
9366 << std::make_shared< QgsStaticExpressionFunction >(
9367 u"to_dm"_s,
9369 << QgsExpressionFunction::Parameter( u"value"_s )
9370 << QgsExpressionFunction::Parameter( u"axis"_s )
9371 << QgsExpressionFunction::Parameter( u"precision"_s )
9372 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9373 fcnToDegreeMinute,
9374 u"Conversions"_s,
9375 QString(),
9376 false,
9377 QSet<QString>(),
9378 false,
9379 QStringList() << u"todm"_s
9380 )
9381 << std::make_shared< QgsStaticExpressionFunction >(
9382 u"to_dms"_s,
9384 << QgsExpressionFunction::Parameter( u"value"_s )
9385 << QgsExpressionFunction::Parameter( u"axis"_s )
9386 << QgsExpressionFunction::Parameter( u"precision"_s )
9387 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9388 fcnToDegreeMinuteSecond,
9389 u"Conversions"_s,
9390 QString(),
9391 false,
9392 QSet<QString>(),
9393 false,
9394 QStringList() << u"todms"_s
9395 )
9396 << std::make_shared<
9397 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 )
9398 << std::make_shared< QgsStaticExpressionFunction >( u"extract_degrees"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
9399 << std::make_shared< QgsStaticExpressionFunction >( u"extract_minutes"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
9400 << std::make_shared< QgsStaticExpressionFunction >( u"extract_seconds"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
9401 << std::make_shared< QgsStaticExpressionFunction >( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9402 << std::make_shared<
9403 QgsStaticExpressionFunction >( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
9404 << std::make_shared< QgsStaticExpressionFunction >(
9405 u"if"_s,
9407 << QgsExpressionFunction::Parameter( u"condition"_s )
9408 << QgsExpressionFunction::Parameter( u"result_when_true"_s )
9409 << QgsExpressionFunction::Parameter( u"result_when_false"_s ),
9410 fcnIf,
9411 u"Conditionals"_s,
9412 QString(),
9413 false,
9414 QSet<QString>(),
9415 true
9416 )
9417 << std::make_shared< QgsStaticExpressionFunction >(
9418 u"try"_s,
9419 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ),
9420 fcnTry,
9421 u"Conditionals"_s,
9422 QString(),
9423 false,
9424 QSet<QString>(),
9425 true
9426 )
9427
9428 << std::make_shared< QgsStaticExpressionFunction >(
9429 u"aggregate"_s,
9431 << QgsExpressionFunction::Parameter( u"layer"_s )
9432 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9433 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9434 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9435 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9436 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9437 fcnAggregate,
9438 u"Aggregates"_s,
9439 QString(),
9440 []( const QgsExpressionNodeFunction *node ) {
9441 // usesGeometry callback: return true if @parent variable is referenced
9442
9443 if ( !node )
9444 return true;
9445
9446 if ( !node->args() )
9447 return false;
9448
9449 QSet<QString> referencedVars;
9450 if ( node->args()->count() > 2 )
9451 {
9452 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9453 referencedVars = subExpressionNode->referencedVariables();
9454 }
9455
9456 if ( node->args()->count() > 3 )
9457 {
9458 QgsExpressionNode *filterNode = node->args()->at( 3 );
9459 referencedVars.unite( filterNode->referencedVariables() );
9460 }
9461 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
9462 },
9463 []( const QgsExpressionNodeFunction *node ) {
9464 // referencedColumns callback: return AllAttributes if @parent variable is referenced
9465
9466 if ( !node )
9467 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9468
9469 if ( !node->args() )
9470 return QSet<QString>();
9471
9472 QSet<QString> referencedCols;
9473 QSet<QString> referencedVars;
9474
9475 if ( node->args()->count() > 2 )
9476 {
9477 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9478 referencedVars = subExpressionNode->referencedVariables();
9479 referencedCols = subExpressionNode->referencedColumns();
9480 }
9481 if ( node->args()->count() > 3 )
9482 {
9483 QgsExpressionNode *filterNode = node->args()->at( 3 );
9484 referencedVars = filterNode->referencedVariables();
9485 referencedCols.unite( filterNode->referencedColumns() );
9486 }
9487
9488 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
9489 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9490 else
9491 return referencedCols;
9492 },
9493 true
9494 )
9495
9496 << std::make_shared< QgsStaticExpressionFunction >(
9497 u"relation_aggregate"_s,
9499 << QgsExpressionFunction::Parameter( u"relation"_s )
9500 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9501 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9502 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9503 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9504 fcnAggregateRelation,
9505 u"Aggregates"_s,
9506 QString(),
9507 false,
9508 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9509 true
9510 )
9511
9512 << std::make_shared< QgsStaticExpressionFunction >( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9513 << std::make_shared< QgsStaticExpressionFunction >( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9514 << std::make_shared< QgsStaticExpressionFunction >( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9515 << std::make_shared< QgsStaticExpressionFunction >( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9516 << std::make_shared< QgsStaticExpressionFunction >( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9517 << std::make_shared< QgsStaticExpressionFunction >( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9518 << std::make_shared< QgsStaticExpressionFunction >( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9519 << std::make_shared< QgsStaticExpressionFunction >( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9520 << std::make_shared< QgsStaticExpressionFunction >( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9521 << std::make_shared< QgsStaticExpressionFunction >( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9522 << std::make_shared< QgsStaticExpressionFunction >( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9523 << std::make_shared< QgsStaticExpressionFunction >( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9524 << std::make_shared< QgsStaticExpressionFunction >( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9525 << std::make_shared< QgsStaticExpressionFunction >( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9526 << std::make_shared< QgsStaticExpressionFunction >( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9527 << std::make_shared< QgsStaticExpressionFunction >( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9528 << std::make_shared< QgsStaticExpressionFunction >( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9529 << std::make_shared< QgsStaticExpressionFunction >( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9530 << std::make_shared< QgsStaticExpressionFunction >( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9531 << std::make_shared< QgsStaticExpressionFunction >( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9532 << std::make_shared< QgsStaticExpressionFunction >( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9533
9534 << std::make_shared<
9535 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 )
9536 << std::make_shared< QgsStaticExpressionFunction >(
9537 u"regexp_matches"_s,
9539 << QgsExpressionFunction::Parameter( u"string"_s )
9540 << QgsExpressionFunction::Parameter( u"regex"_s )
9541 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
9542 fcnRegexpMatches,
9543 u"Arrays"_s
9544 )
9545
9546 << std::make_shared< QgsStaticExpressionFunction >( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9547 << std::make_shared<
9548 QgsStaticExpressionFunction >( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s ) << QgsExpressionFunction::Parameter( u"datetime2"_s ), fcnAge, u"Date and Time"_s )
9549 << std::make_shared< QgsStaticExpressionFunction >( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9550 << std::make_shared< QgsStaticExpressionFunction >( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9551 << std::make_shared< QgsStaticExpressionFunction >( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9552 << std::make_shared< QgsStaticExpressionFunction >( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9553 << std::make_shared< QgsStaticExpressionFunction >( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9554 << std::make_shared< QgsStaticExpressionFunction >( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9555 << std::make_shared< QgsStaticExpressionFunction >( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9556 << std::make_shared< QgsStaticExpressionFunction >( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9557 << std::make_shared< QgsStaticExpressionFunction >( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9558 << std::make_shared< QgsStaticExpressionFunction >( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9559 << std::make_shared< QgsStaticExpressionFunction >(
9560 u"make_date"_s,
9561 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"year"_s ) << QgsExpressionFunction::Parameter( u"month"_s ) << QgsExpressionFunction::Parameter( u"day"_s ),
9562 fcnMakeDate,
9563 u"Date and Time"_s
9564 )
9565 << std::make_shared< QgsStaticExpressionFunction >(
9566 u"make_time"_s,
9567 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s ) << QgsExpressionFunction::Parameter( u"minute"_s ) << QgsExpressionFunction::Parameter( u"second"_s ),
9568 fcnMakeTime,
9569 u"Date and Time"_s
9570 )
9571 << std::make_shared< QgsStaticExpressionFunction >(
9572 u"make_datetime"_s,
9574 << QgsExpressionFunction::Parameter( u"year"_s )
9575 << QgsExpressionFunction::Parameter( u"month"_s )
9576 << QgsExpressionFunction::Parameter( u"day"_s )
9577 << QgsExpressionFunction::Parameter( u"hour"_s )
9578 << QgsExpressionFunction::Parameter( u"minute"_s )
9579 << QgsExpressionFunction::Parameter( u"second"_s ),
9580 fcnMakeDateTime,
9581 u"Date and Time"_s
9582 )
9583 << std::make_shared< QgsStaticExpressionFunction >(
9584 u"make_interval"_s,
9586 << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9587 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9588 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9589 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9590 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9591 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9592 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9593 fcnMakeInterval,
9594 u"Date and Time"_s
9595 )
9596 << std::make_shared< QgsStaticExpressionFunction >( u"timezone_from_id"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9597 << std::make_shared< QgsStaticExpressionFunction >( u"timezone_id"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9598 << std::make_shared< QgsStaticExpressionFunction >( u"get_timezone"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9599 << std::make_shared<
9600 QgsStaticExpressionFunction >( u"set_timezone"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9601 << std::make_shared<
9602 QgsStaticExpressionFunction >( u"convert_timezone"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9603 << std::make_shared< QgsStaticExpressionFunction >( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9604 << std::make_shared< QgsStaticExpressionFunction >(
9605 u"substr_count"_s,
9607 << QgsExpressionFunction::Parameter( u"string"_s )
9608 << QgsExpressionFunction::Parameter( u"substring"_s )
9609 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9610 fcnSubstrCount,
9611 u"String"_s
9612 )
9613 << std::make_shared< QgsStaticExpressionFunction >( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9614 << std::make_shared< QgsStaticExpressionFunction >( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9615 << std::make_shared< QgsStaticExpressionFunction >( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9616 << std::make_shared< QgsStaticExpressionFunction >( u"unaccent"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9617 << std::make_shared<
9618 QgsStaticExpressionFunction >( u"ltrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9619 << std::make_shared<
9620 QgsStaticExpressionFunction >( u"rtrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9621 << std::make_shared<
9622 QgsStaticExpressionFunction >( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9623 << std::make_shared<
9624 QgsStaticExpressionFunction >( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9625 << std::make_shared<
9626 QgsStaticExpressionFunction >( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9627 << std::make_shared< QgsStaticExpressionFunction >( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9628 << std::make_shared< QgsStaticExpressionFunction >( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9629 << std::make_shared< QgsStaticExpressionFunction >( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9630 << std::make_shared< QgsStaticExpressionFunction >(
9631 u"wordwrap"_s,
9633 << QgsExpressionFunction::Parameter( u"text"_s )
9634 << QgsExpressionFunction::Parameter( u"length"_s )
9635 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ),
9636 fcnWordwrap,
9637 u"String"_s
9638 )
9639 << std::make_shared<
9640 QgsStaticExpressionFunction >( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9641 << std::make_shared< QgsStaticExpressionFunction >( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9642 << std::make_shared<
9643 QgsStaticExpressionFunction >( u"repeat"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s ) }, fcnRepeat, u"String"_s )
9644 << std::make_shared< QgsStaticExpressionFunction >( u"replace"_s, -1, fcnReplace, u"String"_s )
9645 << std::make_shared< QgsStaticExpressionFunction >(
9646 u"regexp_replace"_s,
9647 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"replacement"_s ),
9648 fcnRegexpReplace,
9649 u"String"_s
9650 )
9651 << std::make_shared<
9652 QgsStaticExpressionFunction >( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9653 << std::make_shared< QgsStaticExpressionFunction >(
9654 u"substr"_s,
9655 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ),
9656 fcnSubstr,
9657 u"String"_s,
9658 QString(),
9659 false,
9660 QSet< QString >(),
9661 false,
9662 QStringList(),
9663 true
9664 )
9665 << std::make_shared< QgsStaticExpressionFunction >( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9666 << std::make_shared< QgsStaticExpressionFunction >( u"concat_ws"_s, -1, fcnConcatWs, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9667 << std::make_shared<
9668 QgsStaticExpressionFunction >( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9669 << std::make_shared<
9670 QgsStaticExpressionFunction >( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9671 << std::make_shared<
9672 QgsStaticExpressionFunction >( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9673 << std::make_shared< QgsStaticExpressionFunction >(
9674 u"rpad"_s,
9675 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s, true, u" "_s ),
9676 fcnRPad,
9677 u"String"_s
9678 )
9679 << std::make_shared< QgsStaticExpressionFunction >(
9680 u"lpad"_s,
9681 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s, true, u" "_s ),
9682 fcnLPad,
9683 u"String"_s
9684 )
9685 << std::make_shared< QgsStaticExpressionFunction >( u"format"_s, -1, fcnFormatString, u"String"_s )
9686 << std::make_shared< QgsStaticExpressionFunction >(
9687 u"format_number"_s,
9689 << QgsExpressionFunction::Parameter( u"number"_s )
9690 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9691 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9692 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9693 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ),
9694 fcnFormatNumber,
9695 u"String"_s
9696 )
9697 << std::make_shared< QgsStaticExpressionFunction >(
9698 u"format_date"_s,
9700 << QgsExpressionFunction::Parameter( u"datetime"_s )
9701 << QgsExpressionFunction::Parameter( u"format"_s )
9702 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9703 fcnFormatDate,
9704 QStringList() << u"String"_s << u"Date and Time"_s
9705 )
9706 << std::make_shared< QgsStaticExpressionFunction >( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9707 << std::make_shared< QgsStaticExpressionFunction >(
9708 u"color_mix_rgb"_s,
9709 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9710 fcnColorMixRgb,
9711 u"Color"_s
9712 )
9713 << std::make_shared< QgsStaticExpressionFunction >(
9714 u"color_mix"_s,
9715 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9716 fcnColorMix,
9717 u"Color"_s
9718 )
9719 << std::make_shared<
9720 QgsStaticExpressionFunction >( u"color_rgb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"red"_s ) << QgsExpressionFunction::Parameter( u"green"_s ) << QgsExpressionFunction::Parameter( u"blue"_s ), fcnColorRgb, u"Color"_s )
9721 << std::make_shared< QgsStaticExpressionFunction >(
9722 u"color_rgbf"_s,
9724 << QgsExpressionFunction::Parameter( u"red"_s )
9725 << QgsExpressionFunction::Parameter( u"green"_s )
9726 << QgsExpressionFunction::Parameter( u"blue"_s )
9727 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9728 fcnColorRgbF,
9729 u"Color"_s
9730 )
9731 << std::make_shared< QgsStaticExpressionFunction >(
9732 u"color_rgba"_s,
9734 << QgsExpressionFunction::Parameter( u"red"_s )
9735 << QgsExpressionFunction::Parameter( u"green"_s )
9736 << QgsExpressionFunction::Parameter( u"blue"_s )
9737 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9738 fncColorRgba,
9739 u"Color"_s
9740 )
9741 << std::make_shared<
9742 QgsStaticExpressionFunction >( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColor, u"Color"_s )
9743 << std::make_shared<
9744 QgsStaticExpressionFunction >( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColorObject, u"Color"_s )
9745 << std::make_shared<
9746 QgsStaticExpressionFunction >( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ), fcnCreateRamp, u"Color"_s )
9747 << std::make_shared< QgsStaticExpressionFunction >(
9748 u"color_hsl"_s,
9749 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"lightness"_s ),
9750 fcnColorHsl,
9751 u"Color"_s
9752 )
9753 << std::make_shared< QgsStaticExpressionFunction >(
9754 u"color_hsla"_s,
9756 << QgsExpressionFunction::Parameter( u"hue"_s )
9757 << QgsExpressionFunction::Parameter( u"saturation"_s )
9758 << QgsExpressionFunction::Parameter( u"lightness"_s )
9759 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9760 fncColorHsla,
9761 u"Color"_s
9762 )
9763 << std::make_shared< QgsStaticExpressionFunction >(
9764 u"color_hslf"_s,
9766 << QgsExpressionFunction::Parameter( u"hue"_s )
9767 << QgsExpressionFunction::Parameter( u"saturation"_s )
9768 << QgsExpressionFunction::Parameter( u"lightness"_s )
9769 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9770 fcnColorHslF,
9771 u"Color"_s
9772 )
9773 << std::make_shared< QgsStaticExpressionFunction >(
9774 u"color_hsv"_s,
9775 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9776 fcnColorHsv,
9777 u"Color"_s
9778 )
9779 << std::make_shared< QgsStaticExpressionFunction >(
9780 u"color_hsva"_s,
9782 << QgsExpressionFunction::Parameter( u"hue"_s )
9783 << QgsExpressionFunction::Parameter( u"saturation"_s )
9784 << QgsExpressionFunction::Parameter( u"value"_s )
9785 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9786 fncColorHsva,
9787 u"Color"_s
9788 )
9789 << std::make_shared< QgsStaticExpressionFunction >(
9790 u"color_hsvf"_s,
9792 << QgsExpressionFunction::Parameter( u"hue"_s )
9793 << QgsExpressionFunction::Parameter( u"saturation"_s )
9794 << QgsExpressionFunction::Parameter( u"value"_s )
9795 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9796 fcnColorHsvF,
9797 u"Color"_s
9798 )
9799 << std::make_shared< QgsStaticExpressionFunction >(
9800 u"color_cmyk"_s,
9802 << QgsExpressionFunction::Parameter( u"cyan"_s )
9803 << QgsExpressionFunction::Parameter( u"magenta"_s )
9804 << QgsExpressionFunction::Parameter( u"yellow"_s )
9805 << QgsExpressionFunction::Parameter( u"black"_s ),
9806 fcnColorCmyk,
9807 u"Color"_s
9808 )
9809 << std::make_shared< QgsStaticExpressionFunction >(
9810 u"color_cmyka"_s,
9812 << QgsExpressionFunction::Parameter( u"cyan"_s )
9813 << QgsExpressionFunction::Parameter( u"magenta"_s )
9814 << QgsExpressionFunction::Parameter( u"yellow"_s )
9815 << QgsExpressionFunction::Parameter( u"black"_s )
9816 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9817 fncColorCmyka,
9818 u"Color"_s
9819 )
9820 << std::make_shared< QgsStaticExpressionFunction >(
9821 u"color_cmykf"_s,
9823 << QgsExpressionFunction::Parameter( u"cyan"_s )
9824 << QgsExpressionFunction::Parameter( u"magenta"_s )
9825 << QgsExpressionFunction::Parameter( u"yellow"_s )
9826 << QgsExpressionFunction::Parameter( u"black"_s )
9827 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9828 fcnColorCmykF,
9829 u"Color"_s
9830 )
9831 << std::make_shared<
9832 QgsStaticExpressionFunction >( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ), fncColorPart, u"Color"_s )
9833 << std::make_shared<
9834 QgsStaticExpressionFunction >( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncDarker, u"Color"_s )
9835 << std::make_shared<
9836 QgsStaticExpressionFunction >( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncLighter, u"Color"_s )
9837 << std::make_shared< QgsStaticExpressionFunction >(
9838 u"set_color_part"_s,
9839 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9840 fncSetColorPart,
9841 u"Color"_s
9842 )
9843
9844 // file info
9845 << std::make_shared< QgsStaticExpressionFunction >( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnBaseFileName, u"Files and Paths"_s )
9846 << std::make_shared< QgsStaticExpressionFunction >( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSuffix, u"Files and Paths"_s )
9847 << std::make_shared< QgsStaticExpressionFunction >( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileExists, u"Files and Paths"_s )
9848 << std::make_shared< QgsStaticExpressionFunction >( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileName, u"Files and Paths"_s )
9849 << std::make_shared< QgsStaticExpressionFunction >( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsFile, u"Files and Paths"_s )
9850 << std::make_shared< QgsStaticExpressionFunction >( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsDir, u"Files and Paths"_s )
9851 << std::make_shared< QgsStaticExpressionFunction >( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFilePath, u"Files and Paths"_s )
9852 << std::make_shared< QgsStaticExpressionFunction >( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSize, u"Files and Paths"_s )
9853
9854 << std::make_shared<
9855 QgsStaticExpressionFunction >( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ), fcnExif, u"Files and Paths"_s )
9856 << std::make_shared< QgsStaticExpressionFunction >( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnExifGeoTag, u"GeometryGroup"_s )
9857
9858 // hash
9859 << std::make_shared<
9860 QgsStaticExpressionFunction >( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ), fcnGenericHash, u"Conversions"_s )
9861 << std::make_shared< QgsStaticExpressionFunction >( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashMd5, u"Conversions"_s )
9862 << std::make_shared< QgsStaticExpressionFunction >( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashSha256, u"Conversions"_s )
9863
9864 //base64
9865 << std::make_shared< QgsStaticExpressionFunction >( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBase64, u"Conversions"_s )
9866 << std::make_shared< QgsStaticExpressionFunction >( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnFromBase64, u"Conversions"_s )
9867
9868 // magnetic models
9869 << std::make_shared< QgsStaticExpressionFunction >(
9870 u"magnetic_declination"_s,
9872 << QgsExpressionFunction::Parameter( u"model_name"_s )
9873 << QgsExpressionFunction::Parameter( u"date"_s )
9874 << QgsExpressionFunction::Parameter( u"latitude"_s )
9875 << QgsExpressionFunction::Parameter( u"longitude"_s )
9876 << QgsExpressionFunction::Parameter( u"height"_s )
9877 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9878 fcnMagneticDeclination,
9879 u"MagneticModels"_s
9880 )
9881 << std::make_shared< QgsStaticExpressionFunction >(
9882 u"magnetic_inclination"_s,
9884 << QgsExpressionFunction::Parameter( u"model_name"_s )
9885 << QgsExpressionFunction::Parameter( u"date"_s )
9886 << QgsExpressionFunction::Parameter( u"latitude"_s )
9887 << QgsExpressionFunction::Parameter( u"longitude"_s )
9888 << QgsExpressionFunction::Parameter( u"height"_s )
9889 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9890 fcnMagneticInclination,
9891 u"MagneticModels"_s
9892 )
9893 << std::make_shared< QgsStaticExpressionFunction >(
9894 u"magnetic_declination_rate_of_change"_s,
9896 << QgsExpressionFunction::Parameter( u"model_name"_s )
9897 << QgsExpressionFunction::Parameter( u"date"_s )
9898 << QgsExpressionFunction::Parameter( u"latitude"_s )
9899 << QgsExpressionFunction::Parameter( u"longitude"_s )
9900 << QgsExpressionFunction::Parameter( u"height"_s )
9901 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9902 fcnMagneticDeclinationRateOfChange,
9903 u"MagneticModels"_s
9904 )
9905 << std::make_shared< QgsStaticExpressionFunction >(
9906 u"magnetic_inclination_rate_of_change"_s,
9908 << QgsExpressionFunction::Parameter( u"model_name"_s )
9909 << QgsExpressionFunction::Parameter( u"date"_s )
9910 << QgsExpressionFunction::Parameter( u"latitude"_s )
9911 << QgsExpressionFunction::Parameter( u"longitude"_s )
9912 << QgsExpressionFunction::Parameter( u"height"_s )
9913 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9914 fcnMagneticInclinationRateOfChange,
9915 u"MagneticModels"_s
9916 )
9917
9918 // deprecated stuff - hidden from users
9919 << std::make_shared< QgsStaticExpressionFunction >( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9920
9921 auto geomFunc = std::make_shared< QgsStaticExpressionFunction >( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9922 geomFunc->setIsStatic( false );
9923 functions << geomFunc;
9924
9925 auto areaFunc = std::make_shared< QgsStaticExpressionFunction >( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9926 areaFunc->setIsStatic( false );
9927 functions << areaFunc;
9928
9929 functions << std::make_shared< QgsStaticExpressionFunction >( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9930
9931 auto lengthFunc = std::make_shared< QgsStaticExpressionFunction >( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9932 lengthFunc->setIsStatic( false );
9933 functions << lengthFunc;
9934
9935 auto perimeterFunc = std::make_shared< QgsStaticExpressionFunction >( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9936 perimeterFunc->setIsStatic( false );
9937 functions << perimeterFunc;
9938
9939 functions << std::make_shared< QgsStaticExpressionFunction >( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9940
9941 functions << std::make_shared< QgsStaticExpressionFunction >( u"roundness"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnRoundness, u"GeometryGroup"_s );
9942
9943 auto xFunc = std::make_shared< QgsStaticExpressionFunction >( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9944 xFunc->setIsStatic( false );
9945 functions << xFunc;
9946
9947 auto yFunc = std::make_shared< QgsStaticExpressionFunction >( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9948 yFunc->setIsStatic( false );
9949 functions << yFunc;
9950
9951 auto zFunc = std::make_shared< QgsStaticExpressionFunction >( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9952 zFunc->setIsStatic( false );
9953 functions << zFunc;
9954
9955 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions {
9956 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9957 { u"overlay_contains"_s, fcnGeomOverlayContains },
9958 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9959 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9960 { u"overlay_equals_exact"_s, fcnGeomOverlayEqualsExact },
9961 { u"overlay_equals_topological"_s, fcnGeomOverlayEqualsTopological },
9962 { u"overlay_equals_fuzzy"_s, fcnGeomOverlayEqualsFuzzy },
9963 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9964 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9965 { u"overlay_within"_s, fcnGeomOverlayWithin },
9966 };
9967 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9968 while ( i.hasNext() )
9969 {
9970 i.next();
9971 QString defaultBackend = i.key() == "overlay_equals"_L1 ? QString( "QGIS" ) : QString( "GEOS" );
9972 auto fcnGeomOverlayFunc = std::make_shared< QgsStaticExpressionFunction >(
9973 i.key(),
9975 << QgsExpressionFunction::Parameter( u"layer"_s )
9976 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9977 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9978 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9979 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9980 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9981 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9982 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9983 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false )
9984 << QgsExpressionFunction::Parameter( u"backend"_s, true, defaultBackend, false )
9985 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4, false ),
9986 i.value(),
9987 u"GeometryGroup"_s,
9988 QString(),
9989 true,
9990 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9991 true
9992 );
9993
9994 // The current feature is accessed for the geometry, so this should not be cached
9995 fcnGeomOverlayFunc->setIsStatic( false );
9996 functions << fcnGeomOverlayFunc;
9997 }
9998
9999 auto fcnGeomOverlayNearestFunc = std::make_shared< QgsStaticExpressionFunction >(
10000 u"overlay_nearest"_s,
10002 << QgsExpressionFunction::Parameter( u"layer"_s )
10003 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
10004 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
10005 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
10006 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
10007 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
10008 fcnGeomOverlayNearest,
10009 u"GeometryGroup"_s,
10010 QString(),
10011 true,
10012 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
10013 true
10014 );
10015 // The current feature is accessed for the geometry, so this should not be cached
10016 fcnGeomOverlayNearestFunc->setIsStatic( false );
10017 functions << fcnGeomOverlayNearestFunc;
10018
10019 functions
10020 << std::make_shared< QgsStaticExpressionFunction >( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
10021 << std::make_shared< QgsStaticExpressionFunction >( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
10022 << std::make_shared< QgsStaticExpressionFunction >( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
10023 << std::make_shared< QgsStaticExpressionFunction >( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
10024 << std::make_shared< QgsStaticExpressionFunction >( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
10025 << std::make_shared<
10026 QgsStaticExpressionFunction >( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
10027 << std::make_shared< QgsStaticExpressionFunction >( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
10028 << std::make_shared< QgsStaticExpressionFunction >( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
10029 << std::make_shared<
10030 QgsStaticExpressionFunction >( u"nodes_to_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"ignore_closing_nodes"_s, true, false ), fcnNodesToPoints, u"GeometryGroup"_s )
10031 << std::make_shared< QgsStaticExpressionFunction >( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
10032 << std::make_shared< QgsStaticExpressionFunction >( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
10033 << std::make_shared< QgsStaticExpressionFunction >( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
10034 << std::make_shared<
10035 QgsStaticExpressionFunction >( u"make_point_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ) << QgsExpressionFunction::Parameter( u"m"_s ), fcnMakePointM, u"GeometryGroup"_s )
10036 << std::make_shared< QgsStaticExpressionFunction >( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
10037 << std::make_shared< QgsStaticExpressionFunction >( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
10038 << std::make_shared< QgsStaticExpressionFunction >(
10039 u"make_triangle"_s,
10040 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ) << QgsExpressionFunction::Parameter( u"point3"_s ),
10041 fcnMakeTriangle,
10042 u"GeometryGroup"_s
10043 )
10044 << std::make_shared< QgsStaticExpressionFunction >(
10045 u"make_circle"_s,
10047 << QgsExpressionFunction::Parameter( u"center"_s )
10048 << QgsExpressionFunction::Parameter( u"radius"_s )
10049 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
10050 fcnMakeCircle,
10051 u"GeometryGroup"_s
10052 )
10053 << std::make_shared< QgsStaticExpressionFunction >(
10054 u"make_ellipse"_s,
10056 << QgsExpressionFunction::Parameter( u"center"_s )
10057 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
10058 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
10059 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10060 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
10061 fcnMakeEllipse,
10062 u"GeometryGroup"_s
10063 )
10064 << std::make_shared< QgsStaticExpressionFunction >(
10065 u"make_regular_polygon"_s,
10067 << QgsExpressionFunction::Parameter( u"center"_s )
10068 << QgsExpressionFunction::Parameter( u"radius"_s )
10069 << QgsExpressionFunction::Parameter( u"number_sides"_s )
10070 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
10071 fcnMakeRegularPolygon,
10072 u"GeometryGroup"_s
10073 )
10074 << std::make_shared<
10075 QgsStaticExpressionFunction >( u"make_square"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnMakeSquare, u"GeometryGroup"_s )
10076 << std::make_shared< QgsStaticExpressionFunction >(
10077 u"make_rectangle_3points"_s,
10079 << QgsExpressionFunction::Parameter( u"point1"_s )
10080 << QgsExpressionFunction::Parameter( u"point2"_s )
10081 << QgsExpressionFunction::Parameter( u"point3"_s )
10082 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
10083 fcnMakeRectangleFrom3Points,
10084 u"GeometryGroup"_s
10085 )
10086 << std::make_shared< QgsStaticExpressionFunction >(
10087 u"make_valid"_s,
10089 QgsExpressionFunction::Parameter( u"geometry"_s ),
10090#if GEOS_VERSION_NUM < GEOS_COMPUTE_VERSION( 3, 10, 0 )
10091 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
10092#else
10093 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
10094#endif
10095 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
10096 },
10097 fcnGeomMakeValid,
10098 u"GeometryGroup"_s
10099 );
10100
10101 functions << std::make_shared<
10102 QgsStaticExpressionFunction >( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
10103 functions << std::make_shared<
10104 QgsStaticExpressionFunction >( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
10105 functions << std::make_shared<
10106 QgsStaticExpressionFunction >( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
10107 functions << std::make_shared<
10108 QgsStaticExpressionFunction >( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
10109
10110 auto xAtFunc = std::make_shared<
10111 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 );
10112 xAtFunc->setIsStatic( false );
10113 functions << xAtFunc;
10114
10115
10116 auto yAtFunc = std::make_shared<
10117 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 );
10118 yAtFunc->setIsStatic( false );
10119 functions << yAtFunc;
10120
10121 functions
10122 << std::make_shared< QgsStaticExpressionFunction >( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
10123 << std::make_shared<
10124 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 )
10125 << std::make_shared<
10126 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 )
10127 << std::make_shared<
10128 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 )
10129 << std::make_shared<
10130 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 )
10131 << std::make_shared<
10132 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 )
10133 << std::make_shared<
10134 QgsStaticExpressionFunction >( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10135 << std::make_shared<
10136 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 )
10137 << std::make_shared< QgsStaticExpressionFunction >( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
10138 << std::make_shared< QgsStaticExpressionFunction >( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
10139 << std::make_shared< QgsStaticExpressionFunction >(
10140 u"intersects_bbox"_s,
10141 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10142 fcnBbox,
10143 u"GeometryGroup"_s,
10144 QString(),
10145 false,
10146 QSet<QString>(),
10147 false,
10148 QStringList() << u"bbox"_s
10149 )
10150 << std::make_shared<
10151 QgsStaticExpressionFunction >( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDisjoint, u"GeometryGroup"_s )
10152 << std::make_shared<
10153 QgsStaticExpressionFunction >( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersects, u"GeometryGroup"_s )
10154 << std::make_shared<
10155 QgsStaticExpressionFunction >( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnTouches, u"GeometryGroup"_s )
10156 << std::make_shared<
10157 QgsStaticExpressionFunction >( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCrosses, u"GeometryGroup"_s )
10158 << std::make_shared<
10159 QgsStaticExpressionFunction >( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnContains, u"GeometryGroup"_s )
10160 << std::make_shared<
10161 QgsStaticExpressionFunction >( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnOverlaps, u"GeometryGroup"_s )
10162 << std::make_shared<
10163 QgsStaticExpressionFunction >( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnWithin, u"GeometryGroup"_s )
10164 << std::make_shared<
10165 QgsStaticExpressionFunction >( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnEquals, u"GeometryGroup"_s )
10166 << std::make_shared< QgsStaticExpressionFunction >(
10167 u"equals_exact"_s,
10169 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10170 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10171 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s ),
10172 fcnIsEqualsExact,
10173 u"GeometryGroup"_s
10174 )
10175 << std::make_shared< QgsStaticExpressionFunction >(
10176 u"equals_topological"_s,
10178 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10179 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10180 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"GEOS"_s ),
10181 fcnIsEqualsTopological,
10182 u"GeometryGroup"_s
10183 )
10184 << std::make_shared< QgsStaticExpressionFunction >(
10185 u"equals_fuzzy"_s,
10187 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10188 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10189 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s )
10190 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4 ),
10191 fcnIsEqualsFuzzy,
10192 u"GeometryGroup"_s
10193 )
10194 << std::make_shared< QgsStaticExpressionFunction >(
10195 u"translate"_s,
10196 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ),
10197 fcnTranslate,
10198 u"GeometryGroup"_s
10199 )
10200 << std::make_shared< QgsStaticExpressionFunction >(
10201 u"rotate"_s,
10203 << QgsExpressionFunction::Parameter( u"geometry"_s )
10204 << QgsExpressionFunction::Parameter( u"rotation"_s )
10205 << QgsExpressionFunction::Parameter( u"center"_s, true )
10206 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
10207 fcnRotate,
10208 u"GeometryGroup"_s
10209 )
10210 << std::make_shared< QgsStaticExpressionFunction >(
10211 u"scale"_s,
10213 << QgsExpressionFunction::Parameter( u"geometry"_s )
10214 << QgsExpressionFunction::Parameter( u"x_scale"_s )
10215 << QgsExpressionFunction::Parameter( u"y_scale"_s )
10216 << QgsExpressionFunction::Parameter( u"center"_s, true ),
10217 fcnScale,
10218 u"GeometryGroup"_s
10219 )
10220 << std::make_shared< QgsStaticExpressionFunction >(
10221 u"affine_transform"_s,
10223 << QgsExpressionFunction::Parameter( u"geometry"_s )
10224 << QgsExpressionFunction::Parameter( u"delta_x"_s )
10225 << QgsExpressionFunction::Parameter( u"delta_y"_s )
10226 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
10227 << QgsExpressionFunction::Parameter( u"scale_x"_s )
10228 << QgsExpressionFunction::Parameter( u"scale_y"_s )
10229 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
10230 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
10231 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
10232 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
10233 fcnAffineTransform,
10234 u"GeometryGroup"_s
10235 )
10236 << std::make_shared< QgsStaticExpressionFunction >(
10237 u"buffer"_s,
10239 << QgsExpressionFunction::Parameter( u"geometry"_s )
10240 << QgsExpressionFunction::Parameter( u"distance"_s )
10241 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
10242 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
10243 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
10244 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
10245 fcnBuffer,
10246 u"GeometryGroup"_s
10247 )
10248 << std::make_shared< QgsStaticExpressionFunction >( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForceRHR, u"GeometryGroup"_s )
10249 << std::make_shared< QgsStaticExpressionFunction >( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCW, u"GeometryGroup"_s )
10250 << std::make_shared< QgsStaticExpressionFunction >( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCCW, u"GeometryGroup"_s )
10251 << std::make_shared< QgsStaticExpressionFunction >(
10252 u"wedge_buffer"_s,
10254 << QgsExpressionFunction::Parameter( u"center"_s )
10255 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10256 << QgsExpressionFunction::Parameter( u"width"_s )
10257 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
10258 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ),
10259 fcnWedgeBuffer,
10260 u"GeometryGroup"_s
10261 )
10262 << std::make_shared< QgsStaticExpressionFunction >(
10263 u"tapered_buffer"_s,
10265 << QgsExpressionFunction::Parameter( u"geometry"_s )
10266 << QgsExpressionFunction::Parameter( u"start_width"_s )
10267 << QgsExpressionFunction::Parameter( u"end_width"_s )
10268 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ),
10269 fcnTaperedBuffer,
10270 u"GeometryGroup"_s
10271 )
10272 << std::make_shared<
10273 QgsStaticExpressionFunction >( u"buffer_by_m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ), fcnBufferByM, u"GeometryGroup"_s )
10274 << std::make_shared< QgsStaticExpressionFunction >(
10275 u"offset_curve"_s,
10277 << QgsExpressionFunction::Parameter( u"geometry"_s )
10278 << QgsExpressionFunction::Parameter( u"distance"_s )
10279 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10280 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10281 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10282 fcnOffsetCurve,
10283 u"GeometryGroup"_s
10284 )
10285 << std::make_shared< QgsStaticExpressionFunction >(
10286 u"single_sided_buffer"_s,
10288 << QgsExpressionFunction::Parameter( u"geometry"_s )
10289 << QgsExpressionFunction::Parameter( u"distance"_s )
10290 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10291 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10292 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10293 fcnSingleSidedBuffer,
10294 u"GeometryGroup"_s
10295 )
10296 << std::make_shared< QgsStaticExpressionFunction >(
10297 u"extend"_s,
10299 << QgsExpressionFunction::Parameter( u"geometry"_s )
10300 << QgsExpressionFunction::Parameter( u"start_distance"_s )
10301 << QgsExpressionFunction::Parameter( u"end_distance"_s )
10302 << QgsExpressionFunction::Parameter( u"start_deflection"_s, true, 0 )
10303 << QgsExpressionFunction::Parameter( u"end_deflection"_s, true, 0 ),
10304 fcnExtend,
10305 u"GeometryGroup"_s
10306 )
10307 << std::make_shared< QgsStaticExpressionFunction >( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
10308 << std::make_shared< QgsStaticExpressionFunction >( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
10309 << std::make_shared<
10310 QgsStaticExpressionFunction >( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
10311 << std::make_shared<
10312 QgsStaticExpressionFunction >( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, QStringList { u"String"_s, u"GeometryGroup"_s } )
10313 << std::make_shared< QgsStaticExpressionFunction >( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
10314 << std::make_shared<
10315 QgsStaticExpressionFunction >( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnInteriorRingN, u"GeometryGroup"_s )
10316 << std::make_shared<
10317 QgsStaticExpressionFunction >( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnGeometryN, u"GeometryGroup"_s )
10318 << std::make_shared< QgsStaticExpressionFunction >( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
10319 << std::make_shared< QgsStaticExpressionFunction >( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
10320 << std::make_shared<
10321 QgsStaticExpressionFunction >( u"shared_paths"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry1"_s ), QgsExpressionFunction::Parameter( u"geometry2"_s ) }, fcnSharedPaths, u"GeometryGroup"_s )
10322 << std::make_shared< QgsStaticExpressionFunction >( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
10323 << std::make_shared<
10324 QgsStaticExpressionFunction >( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
10325 << std::make_shared<
10326 QgsStaticExpressionFunction >( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
10327 << std::make_shared< QgsStaticExpressionFunction >(
10328 u"smooth"_s,
10330 << QgsExpressionFunction::Parameter( u"geometry"_s )
10331 << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
10332 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
10333 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
10334 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ),
10335 fcnSmooth,
10336 u"GeometryGroup"_s
10337 )
10338 << std::make_shared< QgsStaticExpressionFunction >(
10339 u"triangular_wave"_s,
10340 QgsExpressionFunction::
10341 ParameterList { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"wavelength"_s ), QgsExpressionFunction::Parameter( u"amplitude"_s ), QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10342 fcnTriangularWave,
10343 u"GeometryGroup"_s
10344 )
10345 << std::make_shared< QgsStaticExpressionFunction >(
10346 u"triangular_wave_randomized"_s,
10348 QgsExpressionFunction::Parameter( u"geometry"_s ),
10349 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10350 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10351 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10352 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10353 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
10354 },
10355 fcnTriangularWaveRandomized,
10356 u"GeometryGroup"_s
10357 )
10358 << std::make_shared< QgsStaticExpressionFunction >(
10359 u"square_wave"_s,
10360 QgsExpressionFunction::
10361 ParameterList { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"wavelength"_s ), QgsExpressionFunction::Parameter( u"amplitude"_s ), QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10362 fcnSquareWave,
10363 u"GeometryGroup"_s
10364 )
10365 << std::make_shared< QgsStaticExpressionFunction >(
10366 u"square_wave_randomized"_s,
10368 QgsExpressionFunction::Parameter( u"geometry"_s ),
10369 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10370 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10371 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10372 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10373 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
10374 },
10375 fcnSquareWaveRandomized,
10376 u"GeometryGroup"_s
10377 )
10378 << std::make_shared< QgsStaticExpressionFunction >(
10379 u"wave"_s,
10380 QgsExpressionFunction::
10381 ParameterList { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"wavelength"_s ), QgsExpressionFunction::Parameter( u"amplitude"_s ), QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10382 fcnRoundWave,
10383 u"GeometryGroup"_s
10384 )
10385 << std::make_shared< QgsStaticExpressionFunction >(
10386 u"wave_randomized"_s,
10388 QgsExpressionFunction::Parameter( u"geometry"_s ),
10389 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10390 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10391 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10392 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10393 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 )
10394 },
10395 fcnRoundWaveRandomized,
10396 u"GeometryGroup"_s
10397 )
10398 << std::make_shared< QgsStaticExpressionFunction >(
10399 u"apply_dash_pattern"_s,
10401 QgsExpressionFunction::Parameter( u"geometry"_s ),
10402 QgsExpressionFunction::Parameter( u"pattern"_s ),
10403 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
10404 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
10405 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
10406 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
10407 },
10408 fcnApplyDashPattern,
10409 u"GeometryGroup"_s
10410 )
10411 << std::make_shared<
10412 QgsStaticExpressionFunction >( u"densify_by_count"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"vertices"_s ) }, fcnDensifyByCount, u"GeometryGroup"_s )
10413 << std::make_shared<
10414 QgsStaticExpressionFunction >( u"densify_by_distance"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"distance"_s ) }, fcnDensifyByDistance, u"GeometryGroup"_s )
10415 << std::make_shared< QgsStaticExpressionFunction >( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
10416 << std::make_shared< QgsStaticExpressionFunction >( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
10417 << std::make_shared< QgsStaticExpressionFunction >( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
10418 << std::make_shared< QgsStaticExpressionFunction >( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
10419 << std::make_shared< QgsStaticExpressionFunction >( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
10420 << std::make_shared< QgsStaticExpressionFunction >( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
10421 << std::make_shared< QgsStaticExpressionFunction >( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
10422 << std::make_shared< QgsStaticExpressionFunction >( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
10423 << std::make_shared< QgsStaticExpressionFunction >( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
10424 << std::make_shared< QgsStaticExpressionFunction >(
10425 u"is_empty_or_null"_s,
10426 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
10427 fcnIsEmptyOrNull,
10428 u"GeometryGroup"_s,
10429 QString(),
10430 false,
10431 QSet<QString>(),
10432 false,
10433 QStringList(),
10434 true
10435 )
10436 << std::make_shared<
10437 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 )
10438#if GEOS_VERSION_NUM >= GEOS_COMPUTE_VERSION( 3, 11, 0 )
10439 << std::make_shared< QgsStaticExpressionFunction >(
10440 u"concave_hull"_s,
10442 << QgsExpressionFunction::Parameter( u"geometry"_s )
10443 << QgsExpressionFunction::Parameter( u"target_percent"_s )
10444 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ),
10445 fcnConcaveHull,
10446 u"GeometryGroup"_s
10447 )
10448#endif
10449 << std::make_shared< QgsStaticExpressionFunction >( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnOrientedBBox, u"GeometryGroup"_s )
10450 << std::make_shared< QgsStaticExpressionFunction >( u"main_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMainAngle, u"GeometryGroup"_s )
10451 << std::make_shared<
10452 QgsStaticExpressionFunction >( u"minimal_circle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ), fcnMinimalCircle, u"GeometryGroup"_s )
10453 << std::make_shared<
10454 QgsStaticExpressionFunction >( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDifference, u"GeometryGroup"_s )
10455 << std::make_shared<
10456 QgsStaticExpressionFunction >( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDistance, u"GeometryGroup"_s )
10457 << std::make_shared< QgsStaticExpressionFunction >(
10458 u"hausdorff_distance"_s,
10460 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10461 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10462 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
10463 fcnHausdorffDistance,
10464 u"GeometryGroup"_s
10465 )
10466 << std::make_shared<
10467 QgsStaticExpressionFunction >( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersection, u"GeometryGroup"_s )
10468 << std::make_shared< QgsStaticExpressionFunction >(
10469 u"sym_difference"_s,
10470 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10471 fcnSymDifference,
10472 u"GeometryGroup"_s,
10473 QString(),
10474 false,
10475 QSet<QString>(),
10476 false,
10477 QStringList() << u"symDifference"_s
10478 )
10479 << std::make_shared<
10480 QgsStaticExpressionFunction >( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10481 << std::make_shared<
10482 QgsStaticExpressionFunction >( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10483 << std::make_shared< QgsStaticExpressionFunction >(
10484 u"geom_to_wkt"_s,
10485 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
10486 fcnGeomToWKT,
10487 u"GeometryGroup"_s,
10488 QString(),
10489 false,
10490 QSet<QString>(),
10491 false,
10492 QStringList() << u"geomToWKT"_s
10493 )
10494 << std::make_shared<
10495 QgsStaticExpressionFunction >( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10496 << std::make_shared<
10497 QgsStaticExpressionFunction >( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
10498 << std::make_shared< QgsStaticExpressionFunction >(
10499 u"transform"_s,
10500 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"source_auth_id"_s ) << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
10501 fcnTransformGeometry,
10502 u"GeometryGroup"_s
10503 )
10504 << std::make_shared< QgsStaticExpressionFunction >(
10505 u"extrude"_s,
10506 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ),
10507 fcnExtrude,
10508 u"GeometryGroup"_s,
10509 QString()
10510 )
10511 << std::make_shared< QgsStaticExpressionFunction >( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsMultipart, u"GeometryGroup"_s )
10512 << std::make_shared< QgsStaticExpressionFunction >( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMax, u"GeometryGroup"_s )
10513 << std::make_shared< QgsStaticExpressionFunction >( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMin, u"GeometryGroup"_s )
10514 << std::make_shared< QgsStaticExpressionFunction >( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMax, u"GeometryGroup"_s )
10515 << std::make_shared< QgsStaticExpressionFunction >( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMin, u"GeometryGroup"_s )
10516 << std::make_shared< QgsStaticExpressionFunction >( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSinuosity, u"GeometryGroup"_s )
10517 << std::make_shared<
10518 QgsStaticExpressionFunction >( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStraightDistance2d, u"GeometryGroup"_s );
10519
10520
10521 auto orderPartsFunc = std::make_shared< QgsStaticExpressionFunction >(
10522 u"order_parts"_s,
10524 << QgsExpressionFunction::Parameter( u"geometry"_s )
10525 << QgsExpressionFunction::Parameter( u"orderby"_s )
10526 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
10527 fcnOrderParts,
10528 u"GeometryGroup"_s,
10529 QString()
10530 );
10531
10532 orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10533 const QList< QgsExpressionNode *> argList = node->args()->list();
10534 for ( QgsExpressionNode *argNode : argList )
10535 {
10536 if ( !argNode->isStatic( parent, context ) )
10537 return false;
10538 }
10539
10540 if ( node->args()->count() > 1 )
10541 {
10542 QgsExpressionNode *argNode = node->args()->at( 1 );
10543
10544 QString expString = argNode->eval( parent, context ).toString();
10545
10546 QgsExpression e( expString );
10547
10548 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10549 return true;
10550 }
10551
10552 return true;
10553 } );
10554
10555 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10556 if ( node->args()->count() > 1 )
10557 {
10558 QgsExpressionNode *argNode = node->args()->at( 1 );
10559 QString expression = argNode->eval( parent, context ).toString();
10561 e.prepare( context );
10562 context->setCachedValue( expression, QVariant::fromValue( e ) );
10563 }
10564 return true;
10565 } );
10566 functions << orderPartsFunc;
10567
10568 functions
10569 << std::make_shared<
10570 QgsStaticExpressionFunction >( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnClosestPoint, u"GeometryGroup"_s )
10571 << std::make_shared<
10572 QgsStaticExpressionFunction >( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnShortestLine, u"GeometryGroup"_s )
10573 << std::make_shared<
10574 QgsStaticExpressionFunction >( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
10575 << std::make_shared< QgsStaticExpressionFunction >(
10576 u"line_interpolate_point_by_m"_s,
10578 << QgsExpressionFunction::Parameter( u"geometry"_s )
10579 << QgsExpressionFunction::Parameter( u"m"_s )
10580 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10581 fcnLineInterpolatePointByM,
10582 u"GeometryGroup"_s
10583 )
10584 << std::make_shared<
10585 QgsStaticExpressionFunction >( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
10586 << std::make_shared<
10587 QgsStaticExpressionFunction >( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
10588 << std::make_shared< QgsStaticExpressionFunction >(
10589 u"line_locate_m"_s,
10591 << QgsExpressionFunction::Parameter( u"geometry"_s )
10592 << QgsExpressionFunction::Parameter( u"m"_s )
10593 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10594 fcnLineLocateM,
10595 u"GeometryGroup"_s
10596 )
10597 << std::make_shared<
10598 QgsStaticExpressionFunction >( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
10599 << std::make_shared<
10600 QgsStaticExpressionFunction >( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
10601 << std::make_shared< QgsStaticExpressionFunction >(
10602 u"line_substring"_s,
10603 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10604 fcnLineSubset,
10605 u"GeometryGroup"_s
10606 );
10607
10608
10609 // **Record** functions
10610
10611 auto idFunc = std::make_shared< QgsStaticExpressionFunction >( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
10612 idFunc->setIsStatic( false );
10613 functions << idFunc;
10614
10615 auto currentFeatureFunc = std::make_shared< QgsStaticExpressionFunction >( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
10616 currentFeatureFunc->setIsStatic( false );
10617 functions << currentFeatureFunc;
10618
10619 auto uuidFunc = std::make_shared< QgsStaticExpressionFunction >(
10620 u"uuid"_s,
10621 QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ), QgsExpressionFunction::Parameter( u"version"_s, true, 4 ) },
10622 fcnUuid,
10623 u"Record and Attributes"_s,
10624 QString(),
10625 false,
10626 QSet<QString>(),
10627 false,
10628 QStringList() << u"$uuid"_s
10629 );
10630 uuidFunc->setIsStatic( false );
10631 functions << uuidFunc;
10632
10633 functions
10634 << std::make_shared<
10635 QgsStaticExpressionFunction >( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
10636 << std::make_shared< QgsStaticExpressionFunction >(
10637 u"get_feature"_s,
10638 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"value"_s, true ),
10639 fcnGetFeature,
10640 u"Record and Attributes"_s,
10641 QString(),
10642 false,
10643 QSet<QString>(),
10644 false,
10645 QStringList() << u"QgsExpressionUtils::getFeature"_s
10646 )
10647 << std::make_shared< QgsStaticExpressionFunction >(
10648 u"get_feature_by_id"_s,
10649 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"feature_id"_s ),
10650 fcnGetFeatureById,
10651 u"Record and Attributes"_s,
10652 QString(),
10653 false,
10654 QSet<QString>(),
10655 false
10656 );
10657
10658 auto attributesFunc = std::make_shared< QgsStaticExpressionFunction >(
10659 u"attributes"_s,
10660 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
10661 fcnAttributes,
10662 u"Record and Attributes"_s,
10663 QString(),
10664 false,
10665 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10666 );
10667 attributesFunc->setIsStatic( false );
10668 functions << attributesFunc;
10669 auto representAttributesFunc = std::make_shared<
10670 QgsStaticExpressionFunction >( u"represent_attributes"_s, -1, fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10671 representAttributesFunc->setIsStatic( false );
10672 functions << representAttributesFunc;
10673
10674 auto validateFeature = std::make_shared< QgsStaticExpressionFunction >(
10675 u"is_feature_valid"_s,
10677 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10678 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10679 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10680 fcnValidateFeature,
10681 u"Record and Attributes"_s,
10682 QString(),
10683 false,
10684 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10685 );
10686 validateFeature->setIsStatic( false );
10687 functions << validateFeature;
10688
10689 auto validateAttribute = std::make_shared< QgsStaticExpressionFunction >(
10690 u"is_attribute_valid"_s,
10692 << QgsExpressionFunction::Parameter( u"attribute"_s, false )
10694 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10695 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10696 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10697 fcnValidateAttribute,
10698 u"Record and Attributes"_s,
10699 QString(),
10700 false,
10701 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10702 );
10703 validateAttribute->setIsStatic( false );
10704 functions << validateAttribute;
10705
10706 auto maptipFunc = std::make_shared< QgsStaticExpressionFunction >( u"maptip"_s, -1, fcnFeatureMaptip, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10707 maptipFunc->setIsStatic( false );
10708 functions << maptipFunc;
10709
10710 auto displayFunc = std::make_shared< QgsStaticExpressionFunction >( u"display_expression"_s, -1, fcnFeatureDisplayExpression, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10711 displayFunc->setIsStatic( false );
10712 functions << displayFunc;
10713
10714 auto isSelectedFunc = std::make_shared< QgsStaticExpressionFunction >( u"is_selected"_s, -1, fcnIsSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10715 isSelectedFunc->setIsStatic( false );
10716 functions << isSelectedFunc;
10717
10718 functions << std::make_shared< QgsStaticExpressionFunction >( u"num_selected"_s, -1, fcnNumSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10719
10720 functions << std::make_shared< QgsStaticExpressionFunction >(
10721 u"sqlite_fetch_and_increment"_s,
10723 << QgsExpressionFunction::Parameter( u"database"_s )
10724 << QgsExpressionFunction::Parameter( u"table"_s )
10725 << QgsExpressionFunction::Parameter( u"id_field"_s )
10726 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
10727 << QgsExpressionFunction::Parameter( u"filter_value"_s )
10728 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
10729 fcnSqliteFetchAndIncrement,
10730 u"Record and Attributes"_s
10731 );
10732
10733 // **CRS** functions
10734 functions
10735 << std::make_shared< QgsStaticExpressionFunction >( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
10736 << std::make_shared< QgsStaticExpressionFunction >( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
10737
10738
10739 // **Fields and Values** functions
10740 auto representValueFunc = std::make_shared<
10741 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 );
10742
10743 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10744 Q_UNUSED( context )
10745 if ( node->args()->count() == 1 )
10746 {
10747 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
10748 if ( colRef )
10749 {
10750 return true;
10751 }
10752 else
10753 {
10754 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
10755 return false;
10756 }
10757 }
10758 else if ( node->args()->count() == 2 )
10759 {
10760 return true;
10761 }
10762 else
10763 {
10764 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
10765 return false;
10766 }
10767 } );
10768
10769 functions << representValueFunc;
10770
10771 // **General** functions
10772 functions
10773 << std::make_shared< QgsStaticExpressionFunction >(
10774 u"layer_property"_s,
10776 << QgsExpressionFunction::Parameter( u"layer"_s )
10777 << QgsExpressionFunction::Parameter( u"property"_s )
10778 << QgsExpressionFunction::Parameter( u"translate"_s, true, true ),
10779 fcnGetLayerProperty,
10780 u"Map Layers"_s
10781 )
10782 << std::make_shared<
10783 QgsStaticExpressionFunction >( u"decode_uri"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"part"_s, true ), fcnDecodeUri, u"Map Layers"_s )
10784 << std::make_shared< QgsStaticExpressionFunction >( u"mime_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary_data"_s ), fcnMimeType, u"General"_s )
10785 << std::make_shared< QgsStaticExpressionFunction >(
10786 u"raster_statistic"_s,
10787 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"statistic"_s ),
10788 fcnGetRasterBandStat,
10789 u"Rasters"_s
10790 );
10791
10792 // **var** function
10793 auto varFunction = std::make_shared< QgsStaticExpressionFunction >( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
10794 varFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10795 /* A variable node is static if it has a static name and the name can be found at prepare
10796 * time and is tagged with isStatic.
10797 * It is not static if a variable is set during iteration or not tagged isStatic.
10798 * (e.g. geom_part variable)
10799 */
10800 if ( node->args()->count() > 0 )
10801 {
10802 QgsExpressionNode *argNode = node->args()->at( 0 );
10803
10804 if ( !argNode->isStatic( parent, context ) )
10805 return false;
10806
10807 const QString varName = argNode->eval( parent, context ).toString();
10808 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
10809 return false;
10810
10811 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
10812 return scope ? scope->isStatic( varName ) : false;
10813 }
10814 return false;
10815 } );
10816 varFunction->setUsesGeometryFunction( []( const QgsExpressionNodeFunction *node ) -> bool {
10817 if ( node && node->args()->count() > 0 )
10818 {
10819 QgsExpressionNode *argNode = node->args()->at( 0 );
10820 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
10821 {
10822 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
10823 return true;
10824 }
10825 }
10826 return false;
10827 } );
10828
10829 functions << varFunction;
10830
10831 auto evalTemplateFunction = std::make_shared<
10832 QgsStaticExpressionFunction >( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10833 evalTemplateFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10834 if ( node->args()->count() > 0 )
10835 {
10836 QgsExpressionNode *argNode = node->args()->at( 0 );
10837
10838 if ( argNode->isStatic( parent, context ) )
10839 {
10840 QString expString = argNode->eval( parent, context ).toString();
10841
10842 QgsExpression e( expString );
10843
10844 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10845 return true;
10846 }
10847 }
10848
10849 return false;
10850 } );
10851 functions << evalTemplateFunction;
10852
10853 auto evalFunc = std::make_shared<
10854 QgsStaticExpressionFunction >( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10855 evalFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10856 if ( node->args()->count() > 0 )
10857 {
10858 QgsExpressionNode *argNode = node->args()->at( 0 );
10859
10860 if ( argNode->isStatic( parent, context ) )
10861 {
10862 QString expString = argNode->eval( parent, context ).toString();
10863
10864 QgsExpression e( expString );
10865
10866 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10867 return true;
10868 }
10869 }
10870
10871 return false;
10872 } );
10873
10874 functions << evalFunc;
10875
10876 auto attributeFunc
10877 = std::make_shared< QgsStaticExpressionFunction >( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10878 attributeFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10879 const QList< QgsExpressionNode *> argList = node->args()->list();
10880 for ( QgsExpressionNode *argNode : argList )
10881 {
10882 if ( !argNode->isStatic( parent, context ) )
10883 return false;
10884 }
10885
10886 if ( node->args()->count() == 1 )
10887 {
10888 // not static -- this is the variant which uses the current feature taken direct from the expression context
10889 return false;
10890 }
10891
10892 return true;
10893 } );
10894 functions << attributeFunc;
10895
10896 functions
10897 << std::make_shared< QgsStaticExpressionFunction >( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10898 << std::make_shared< QgsWithVariableExpressionFunction >()
10899 << std::make_shared< QgsStaticExpressionFunction >(
10900 u"raster_value"_s,
10901 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10902 fcnRasterValue,
10903 u"Rasters"_s
10904 )
10905 << std::make_shared< QgsStaticExpressionFunction >(
10906 u"raster_attributes"_s,
10907 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10908 fcnRasterAttributes,
10909 u"Rasters"_s
10910 )
10911
10912 // functions for arrays
10913 << std::make_shared< QgsArrayForeachExpressionFunction >()
10914 << std::make_shared< QgsArrayFilterExpressionFunction >()
10915 << std::make_shared< QgsStaticExpressionFunction >( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10916 << std::make_shared<
10917 QgsStaticExpressionFunction >( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10918 << std::make_shared< QgsStaticExpressionFunction >( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10919 << std::make_shared<
10920 QgsStaticExpressionFunction >( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10921 << std::make_shared<
10922 QgsStaticExpressionFunction >( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10923 << std::make_shared<
10924 QgsStaticExpressionFunction >( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayAll, u"Arrays"_s )
10925 << std::make_shared<
10926 QgsStaticExpressionFunction >( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10927 << std::make_shared<
10928 QgsStaticExpressionFunction >( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10929 << std::make_shared< QgsStaticExpressionFunction >( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10930 << std::make_shared< QgsStaticExpressionFunction >( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10931 << std::make_shared< QgsStaticExpressionFunction >( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10932 << std::make_shared< QgsStaticExpressionFunction >( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10933 << std::make_shared< QgsStaticExpressionFunction >( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10934 << std::make_shared< QgsStaticExpressionFunction >( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10935 << std::make_shared<
10936 QgsStaticExpressionFunction >( u"array_majority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMajority, u"Arrays"_s )
10937 << std::make_shared<
10938 QgsStaticExpressionFunction >( u"array_minority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMinority, u"Arrays"_s )
10939 << std::make_shared< QgsStaticExpressionFunction >( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10940 << std::make_shared<
10941 QgsStaticExpressionFunction >( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10942 << std::make_shared<
10943 QgsStaticExpressionFunction >( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10944 << std::make_shared< QgsStaticExpressionFunction >(
10945 u"array_insert"_s,
10946 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10947 fcnArrayInsert,
10948 u"Arrays"_s
10949 )
10950 << std::make_shared<
10951 QgsStaticExpressionFunction >( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10952 << std::make_shared< QgsStaticExpressionFunction >(
10953 u"array_remove_all"_s,
10954 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10955 fcnArrayRemoveAll,
10956 u"Arrays"_s,
10957 QString(),
10958 false,
10959 QSet<QString>(),
10960 false,
10961 QStringList(),
10962 true
10963 )
10964 << std::make_shared< QgsStaticExpressionFunction >( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10965 << std::make_shared<
10966 QgsStaticExpressionFunction >( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"priority"_s ), fcnArrayPrioritize, u"Arrays"_s )
10967 << std::make_shared< QgsStaticExpressionFunction >( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10968 << std::make_shared< QgsStaticExpressionFunction >(
10969 u"array_slice"_s,
10970 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ),
10971 fcnArraySlice,
10972 u"Arrays"_s
10973 )
10974 << std::make_shared< QgsStaticExpressionFunction >( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10975 << std::make_shared<
10976 QgsStaticExpressionFunction >( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10977 << std::make_shared< QgsStaticExpressionFunction >( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10978 << std::make_shared< QgsStaticExpressionFunction >(
10979 u"array_to_string"_s,
10981 << QgsExpressionFunction::Parameter( u"array"_s )
10982 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10983 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10984 fcnArrayToString,
10985 u"Arrays"_s
10986 )
10987 << std::make_shared< QgsStaticExpressionFunction >(
10988 u"string_to_array"_s,
10990 << QgsExpressionFunction::Parameter( u"string"_s )
10991 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10992 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10993 fcnStringToArray,
10994 u"Arrays"_s
10995 )
10996 << std::make_shared< QgsStaticExpressionFunction >(
10997 u"generate_series"_s,
10998 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ),
10999 fcnGenerateSeries,
11000 u"Arrays"_s
11001 )
11002 << std::make_shared<
11003 QgsStaticExpressionFunction >( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
11004
11005 //functions for maps
11006 << std::make_shared<
11007 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 )
11008 << std::make_shared<
11009 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 )
11010 << std::make_shared< QgsStaticExpressionFunction >( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
11011 << std::make_shared< QgsStaticExpressionFunction >( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
11012 << std::make_shared< QgsStaticExpressionFunction >( u"map"_s, -1, fcnMap, u"Maps"_s )
11013 << std::make_shared<
11014 QgsStaticExpressionFunction >( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
11015 << std::make_shared<
11016 QgsStaticExpressionFunction >( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
11017 << std::make_shared<
11018 QgsStaticExpressionFunction >( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
11019 << std::make_shared<
11020 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 )
11021 << std::make_shared< QgsStaticExpressionFunction >( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
11022 << std::make_shared< QgsStaticExpressionFunction >( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
11023 << std::make_shared< QgsStaticExpressionFunction >( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
11024 << std::make_shared<
11025 QgsStaticExpressionFunction >( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"prefix"_s ), fcnMapPrefixKeys, u"Maps"_s )
11026 << std::make_shared< QgsStaticExpressionFunction >( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlTable, u"Maps"_s )
11027 << std::make_shared< QgsStaticExpressionFunction >( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlDefinitionList, u"Maps"_s )
11028 << std::make_shared< QgsStaticExpressionFunction >( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnToFormUrlEncode, u"Maps"_s )
11029
11030 ;
11031
11033
11034 for ( const SharedFunctionPtr &function : std::as_const( functions ) )
11035 {
11036 sBuiltinFunctionNames << function->name();
11037 sBuiltinFunctionNames.append( function->aliases() );
11038 }
11039
11040 functions.updateFunctionNameMap();
11041
11042 sFunctions.store( std::make_shared<SharedFunctionPtrList>( functions ), std::memory_order_release );
11043 } );
11044
11045 return sFunctions.load( std::memory_order_acquire );
11046}
11047
11048bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
11049{
11050 QMutexLocker lock( &sFunctionWriteMutex );
11051
11052 const QgsExpression::FunctionListSnapshot currentFunctions = sFunctions.load( std::memory_order_relaxed );
11053 if ( currentFunctions->functionIndex( function->name() ) != -1 )
11054 {
11055 return false;
11056 }
11057
11058 // make a copy of the list of shared pointers (this doesn't actually copy the functions, only the *shared_ptrs*!)
11059 auto newSnapshot = std::make_shared<SharedFunctionPtrList>( *currentFunctions );
11060
11061 if ( transferOwnership )
11062 {
11063 SharedFunctionPtr functionPtr( function );
11064 newSnapshot->append( functionPtr );
11065 }
11066 else
11067 {
11068 SharedFunctionPtr unownedFunctionPtr( function, []( const QgsExpressionFunction * ) {
11069 // custom deleter:
11070 // when final reference to function is removed DO NOTHING, do not delete
11071 // the function we don't own
11072 } );
11073 newSnapshot->append( unownedFunctionPtr );
11074 }
11075
11076 newSnapshot->updateFunctionNameMap();
11077
11078 // atomically store -- any existing references to the old list (without the new pointer) will still
11079 // be safe to use
11080 sFunctions.store( newSnapshot, std::memory_order_release );
11081 return true;
11082}
11083
11084bool QgsExpression::unregisterFunction( const QString &name )
11085{
11086 // You can never override the built in functions.
11087 if ( QgsExpression::BuiltinFunctions().contains( name ) )
11088 {
11089 return false;
11090 }
11091
11092 QMutexLocker lock( &sFunctionWriteMutex );
11093
11094 const QgsExpression::FunctionListSnapshot currentFunctions = sFunctions.load( std::memory_order_relaxed );
11095 auto newSnapshot = std::make_shared<SharedFunctionPtrList>();
11096
11097 for ( const SharedFunctionPtr &function : *currentFunctions )
11098 {
11099 if ( function->name() != name )
11100 {
11101 newSnapshot->append( function );
11102 }
11103 }
11104 newSnapshot->updateFunctionNameMap();
11105
11106 // atomically store -- any existing references to the old list (without the new pointer) will still
11107 // be safe to use
11108 sFunctions.store( newSnapshot, std::memory_order_release );
11109 return true;
11110}
11111
11113{
11114 QMutexLocker lock( &sFunctionWriteMutex );
11115
11116 sBuiltinFunctionNames.clear();
11117
11118 // atomically store -- any existing references to the old list (without the new pointer) will still
11119 // be safe to use
11120 // when those existing references are deleted (or there weren't any to begin with), then there'll be
11121 // no more references to the function pointers after this next, and they'll be automatically deleted
11122 // (if they were registered without ownership transfer then the shared_ptr deleter for those items
11123 // is a no-op, so we won't try to actually delete the pointed object for those)
11124 sFunctions.store( std::make_shared<SharedFunctionPtrList>(), std::memory_order_release );
11125}
11126
11128{
11129 // we don't need a mutex for this -- this list is populated ONCE at startup, and never changed
11130 // outside of cleanRegisteredFunctions (which is only called at application tear down).
11131 return sBuiltinFunctionNames;
11132}
11133
11136 u"array_foreach"_s, // skip-keyword-check
11137 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ),
11138 u"Arrays"_s
11139 )
11140{}
11141
11143{
11144 bool isStatic = false;
11145
11146 QgsExpressionNode::NodeList *args = node->args();
11148 if ( args->count() < 2 )
11149 return false;
11150
11151 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11152 {
11153 isStatic = true;
11154 }
11155 return isStatic;
11156}
11157
11159{
11160 Q_UNUSED( node )
11161 QVariantList result;
11162
11163 if ( args->count() < 2 )
11164 // error
11165 return result;
11166
11167 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11168
11169 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11170 std::unique_ptr< QgsExpressionContext > tempContext;
11171 if ( !subContext )
11172 {
11173 tempContext = std::make_unique< QgsExpressionContext >();
11174 subContext = tempContext.get();
11175 }
11176
11177 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11178 subContext->appendScope( subScope );
11179
11180 int i = 0;
11181 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
11182 {
11183 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
11184 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
11185 result << args->at( 1 )->eval( parent, subContext );
11186 }
11187
11188 if ( context )
11189 delete subContext->popScope();
11190
11191 return result;
11192}
11193
11194QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11196 // This is a dummy function, all the real handling is in run
11197 Q_UNUSED( values )
11198 Q_UNUSED( context )
11199 Q_UNUSED( parent )
11200 Q_UNUSED( node )
11201
11202 Q_ASSERT( false );
11203 return QVariant();
11204}
11205
11207{
11208 QgsExpressionNode::NodeList *args = node->args();
11209
11210 if ( args->count() < 2 )
11211 // error
11212 return false;
11213
11214 args->at( 0 )->prepare( parent, context );
11215
11216 QgsExpressionContext subContext;
11217 if ( context )
11218 subContext = *context;
11221 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11222 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
11223 subContext.appendScope( subScope );
11224
11225 args->at( 1 )->prepare( parent, &subContext );
11226
11227 return true;
11228}
11229
11231 : QgsExpressionFunction( u"array_filter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"limit"_s, true, 0 ), u"Arrays"_s )
11232{}
11233
11235{
11236 bool isStatic = false;
11237
11238 QgsExpressionNode::NodeList *args = node->args();
11240 if ( args->count() < 2 )
11241 return false;
11242
11243 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11244 {
11245 isStatic = true;
11246 }
11247 return isStatic;
11248}
11249
11251{
11252 Q_UNUSED( node )
11253 QVariantList result;
11254
11255 if ( args->count() < 2 )
11256 // error
11257 return result;
11258
11259 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11260
11261 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11262 std::unique_ptr< QgsExpressionContext > tempContext;
11263 if ( !subContext )
11264 {
11265 tempContext = std::make_unique< QgsExpressionContext >();
11266 subContext = tempContext.get();
11267 }
11268
11269 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11270 subContext->appendScope( subScope );
11271
11272 int limit = 0;
11273 if ( args->count() >= 3 )
11274 {
11275 const QVariant limitVar = args->at( 2 )->eval( parent, context );
11276
11277 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
11278 {
11279 limit = limitVar.toInt();
11280 }
11281 else
11282 {
11283 return result;
11284 }
11285 }
11286
11287 for ( const QVariant &value : array )
11288 {
11289 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
11290 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
11291 {
11292 result << value;
11293
11294 if ( limit > 0 && limit == result.size() )
11295 break;
11296 }
11297 }
11298
11299 if ( context )
11300 delete subContext->popScope();
11301
11302 return result;
11303}
11304
11305QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11307 // This is a dummy function, all the real handling is in run
11308 Q_UNUSED( values )
11309 Q_UNUSED( context )
11310 Q_UNUSED( parent )
11311 Q_UNUSED( node )
11312
11313 Q_ASSERT( false );
11314 return QVariant();
11315}
11316
11318{
11319 QgsExpressionNode::NodeList *args = node->args();
11320
11321 if ( args->count() < 2 )
11322 // error
11323 return false;
11324
11325 args->at( 0 )->prepare( parent, context );
11326
11327 QgsExpressionContext subContext;
11328 if ( context )
11329 subContext = *context;
11330
11332 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11333 subContext.appendScope( subScope );
11334
11335 args->at( 1 )->prepare( parent, &subContext );
11336
11337 return true;
11338}
11340 : QgsExpressionFunction( u"with_variable"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ), u"General"_s )
11341{}
11342
11344{
11345 bool isStatic = false;
11346
11347 QgsExpressionNode::NodeList *args = node->args();
11348
11349 if ( args->count() < 3 )
11350 return false;
11351
11352 // We only need to check if the node evaluation is static, if both - name and value - are static.
11353 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11354 {
11355 QVariant name = args->at( 0 )->eval( parent, context );
11356 QVariant value = args->at( 1 )->eval( parent, context );
11358 // Temporarily append a new scope to provide the variable
11359 appendTemporaryVariable( context, name.toString(), value );
11360 if ( args->at( 2 )->isStatic( parent, context ) )
11361 isStatic = true;
11362 popTemporaryVariable( context );
11363 }
11364
11365 return isStatic;
11366}
11367
11369{
11370 Q_UNUSED( node )
11371 QVariant result;
11372
11373 if ( args->count() < 3 )
11374 // error
11375 return result;
11376
11377 QVariant name = args->at( 0 )->eval( parent, context );
11378 QVariant value = args->at( 1 )->eval( parent, context );
11379
11380 const QgsExpressionContext *updatedContext = context;
11381 std::unique_ptr< QgsExpressionContext > tempContext;
11382 if ( !updatedContext )
11383 {
11384 tempContext = std::make_unique< QgsExpressionContext >();
11385 updatedContext = tempContext.get();
11387
11388 appendTemporaryVariable( updatedContext, name.toString(), value );
11389 result = args->at( 2 )->eval( parent, updatedContext );
11390
11391 if ( context )
11392 popTemporaryVariable( updatedContext );
11393
11394 return result;
11395}
11396
11397QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11399 // This is a dummy function, all the real handling is in run
11400 Q_UNUSED( values )
11401 Q_UNUSED( context )
11402 Q_UNUSED( parent )
11403 Q_UNUSED( node )
11404
11405 Q_ASSERT( false );
11406 return QVariant();
11407}
11408
11410{
11411 QgsExpressionNode::NodeList *args = node->args();
11412
11413 if ( args->count() < 3 )
11414 // error
11415 return false;
11416
11417 QVariant name = args->at( 0 )->prepare( parent, context );
11418 QVariant value = args->at( 1 )->prepare( parent, context );
11419
11420 const QgsExpressionContext *updatedContext = context;
11421 std::unique_ptr< QgsExpressionContext > tempContext;
11422 if ( !updatedContext )
11423 {
11424 tempContext = std::make_unique< QgsExpressionContext >();
11425 updatedContext = tempContext.get();
11426 }
11427
11428 appendTemporaryVariable( updatedContext, name.toString(), value );
11429 args->at( 2 )->prepare( parent, updatedContext );
11430
11431 if ( context )
11432 popTemporaryVariable( updatedContext );
11433
11434 return true;
11435}
11436
11437void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
11438{
11439 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11440 delete updatedContext->popScope();
11441}
11442
11443void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
11444{
11445 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
11446 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
11447
11448 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11449 updatedContext->appendScope( scope );
11450}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2319
@ GEOS
Use GEOS implementation.
Definition qgis.h:2321
@ QGIS
Use internal implementation.
Definition qgis.h:2320
@ Left
Buffer to left of line.
Definition qgis.h:2269
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3529
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3531
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3530
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3532
@ Success
Operation succeeded.
Definition qgis.h:2213
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3241
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2380
@ Point
Points.
Definition qgis.h:400
@ Line
Lines.
Definition qgis.h:401
@ Polygon
Polygons.
Definition qgis.h:402
@ Unknown
Unknown types.
Definition qgis.h:403
@ Null
No geometry.
Definition qgis.h:404
JoinStyle
Join styles for buffers.
Definition qgis.h:2293
@ Bevel
Use beveled joins.
Definition qgis.h:2296
@ Round
Use rounded joins.
Definition qgis.h:2294
@ Miter
Use mitered joins.
Definition qgis.h:2295
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6761
@ StdDev
Standard deviation.
Definition qgis.h:6768
@ NoStatistic
No statistic.
Definition qgis.h:6762
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:214
@ Plugin
Plugin based layer.
Definition qgis.h:209
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2280
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2282
@ Round
Round cap.
Definition qgis.h:2281
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2283
Aggregate
Available aggregates to calculate.
Definition qgis.h:6638
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6655
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6652
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6645
@ Median
Median of values (numeric fields only).
Definition qgis.h:6646
@ Max
Max of values.
Definition qgis.h:6643
@ Min
Min of values.
Definition qgis.h:6642
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6656
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6649
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6660
@ Sum
Sum of values.
Definition qgis.h:6644
@ Minority
Minority of values.
Definition qgis.h:6650
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6641
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6659
@ Majority
Majority of values.
Definition qgis.h:6651
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6648
@ Count
Count.
Definition qgis.h:6639
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6653
@ CountDistinct
Number of distinct values.
Definition qgis.h:6640
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6657
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6658
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6654
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3514
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3517
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3519
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3518
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3516
@ NoRule
No special rule.
Definition qgis.h:3515
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2364
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2365
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2366
@ Point
Point.
Definition qgis.h:316
@ PointM
PointM.
Definition qgis.h:349
@ PointZ
PointZ.
Definition qgis.h:333
@ GeometryCollection
GeometryCollection.
Definition qgis.h:323
@ PointZM
PointZM.
Definition qgis.h:365
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:46
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:282
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:277
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:41
Defines a QGIS exception class.
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 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(QgsExpression::SharedFunctionPtrList &functions)
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.
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.
A thread-safe container of shared pointers to registered 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.
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.
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
std::shared_ptr< const SharedFunctionPtrList > FunctionListSnapshot
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 const FunctionListSnapshot getFunctionsSnapshot()
Returns a thread-safe snapshot of all registered functions.
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.
std::shared_ptr< QgsExpressionFunction > SharedFunctionPtr
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
static QStringList BuiltinFunctions()
Returns a list of the names of all built-in functions.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:65
QgsFeatureId id
Definition qgsfeature.h:63
QgsGeometry geometry
Definition qgsfeature.h:66
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:754
Container of fields for a vector layer.
Definition qgsfields.h:45
int count
Definition qgsfields.h:49
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.
Encapsulates parameters under which a geometry operation is performed.
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 intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points shared by this geometry and 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 concaveHull(double targetPercent, bool allowHoles=false, QgsFeedback *feedback=nullptr) const
Returns a possibly concave polygon that contains all the points in the geometry.
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.
Q_INVOKABLE bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
bool isExactlyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry using the specified backend.
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.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
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.
Q_INVOKABLE 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 symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
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 isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry extendLine(double startDistance, double endDistance, double startDeflection=0, double endDeflection=0) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
Q_INVOKABLE 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.
Q_INVOKABLE 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 combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QgsFeedback *feedback=nullptr) const
Attempts to make an invalid geometry valid without losing vertices.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry 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, QgsFeedback *feedback=nullptr) 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.
bool isFuzzyEqual(const QgsGeometry &geometry, double epsilon=1e-4, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry within the tolerance epsilon using the specified backend.
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 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 ...
bool isTopologicallyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::GEOS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometry simplify(double tolerance, QgsFeedback *feedback=nullptr) 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...
Q_INVOKABLE 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.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
Q_INVOKABLE 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:192
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:3439
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:52
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.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Represents a model of the Earth's magnetic field.
static bool fieldComponentsWithTimeDerivatives(double Bx, double By, double Bz, double Bxt, double Byt, double Bzt, double &H, double &F, double &D, double &I, double &Ht, double &Ft, double &Dt, double &It)
Compute various quantities dependent on a magnetic field and their rates of change.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
virtual Q_INVOKABLE QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:86
QgsLayerMetadata metadata
Definition qgsmaplayer.h:89
Qgis::LayerType type
Definition qgsmaplayer.h:93
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString mapTipTemplate
Definition qgsmaplayer.h:96
Implementation of a geometry simplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:245
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:731
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:597
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:460
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:138
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
double m
Definition qgspoint.h:59
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:743
double y
Definition qgspoint.h:57
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsRelationManager * relationManager
Definition qgsproject.h:125
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:543
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:164
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
Decodes a string to a color.
static QString encodeColor(const QColor &color)
Lossy-encodes a color to a string.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:596
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:8036
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8379
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8378
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7837
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7743
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)
std::function< bool(const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend)> RelationFunction
allows geometry function with different parameters to be used with the same executeGeomOverlay functi...
double qDateTimeToDecimalYear(const QDateTime &dateTime)
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 *)
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
#define SET_EVAL_ERROR(x)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:98
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
#define GEOS_COMPUTE_VERSION(major, minor, patch)
compute GEOS integer version from major, minor and patch number
Definition qgsgeos.h:38
#define GEOS_VERSION_NUM
compute current GEOS integer version
Definition qgsgeos.h:42
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:63
const QgsMapLayer * layer
Definition qgsogcutils.h:71
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:72
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:35