QGIS API Documentation 4.1.0-Master (31622b25bb0)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
45#include "qgsmagneticmodel.h"
47#include "qgsmessagelog.h"
48#include "qgsmultilinestring.h"
49#include "qgsmultipoint.h"
50#include "qgsogcutils.h"
51#include "qgspolygon.h"
52#include "qgsproviderregistry.h"
53#include "qgsquadrilateral.h"
54#include "qgsrasterbandstats.h"
55#include "qgsrasterlayer.h"
56#include "qgsregularpolygon.h"
57#include "qgsspatialindex.h"
58#include "qgsstringutils.h"
59#include "qgsstyle.h"
60#include "qgssymbollayerutils.h"
61#include "qgsthreadingutils.h"
62#include "qgstransaction.h"
63#include "qgstriangle.h"
64#include "qgsunittypes.h"
65#include "qgsvariantutils.h"
66#include "qgsvectorlayer.h"
68#include "qgsvectorlayerutils.h"
69
70#include <QCryptographicHash>
71#include <QMimeDatabase>
72#include <QProcessEnvironment>
73#include <QRegularExpression>
74#include <QString>
75#include <QUrlQuery>
76#include <QUuid>
77
78using namespace Qt::StringLiterals;
79
80typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
81
83Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
85
88Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
89
90const QString QgsExpressionFunction::helpText() const
91{
92 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
93}
94
96{
97 Q_UNUSED( node )
98 // evaluate arguments
99 QVariantList argValues;
100 if ( args )
101 {
102 int arg = 0;
103 const QList< QgsExpressionNode * > argList = args->list();
104 argValues.reserve( argList.size() );
105 for ( QgsExpressionNode *n : argList )
106 {
107 QVariant v;
108 if ( lazyEval() )
109 {
110 // Pass in the node for the function to eval as it needs.
111 v = QVariant::fromValue( n );
112 }
113 else
114 {
115 v = n->eval( parent, context );
117 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
118 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
119 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
120 }
121 argValues.append( v );
122 arg++;
123 }
124 }
125
126 return func( argValues, context, parent, node );
127}
128
130{
131 Q_UNUSED( node )
132 return true;
133}
134
136{
137 return QStringList();
138}
139
141{
142 Q_UNUSED( parent )
143 Q_UNUSED( context )
144 Q_UNUSED( node )
145 return false;
146}
147
149{
150 Q_UNUSED( parent )
151 Q_UNUSED( context )
152 Q_UNUSED( node )
153 return true;
154}
155
157{
158 Q_UNUSED( node )
159 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
160}
161
163{
164 return mGroups.isEmpty() ? false : mGroups.contains( u"deprecated"_s );
165}
166
168{
169 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
170}
171
173{
174 return mHandlesNull;
175}
176
177// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
180 const QString &fnname,
182 FcnEval fcn,
183 const QString &group,
184 const QString &helpText,
185 const std::function< bool( const QgsExpressionNodeFunction *node ) > &usesGeometry,
186 const std::function< QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
187 bool lazyEval,
188 const QStringList &aliases,
189 bool handlesNull
190)
191 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
192 , mFnc( fcn )
193 , mAliases( aliases )
194 , mUsesGeometry( false )
195 , mUsesGeometryFunc( usesGeometry )
196 , mReferencedColumnsFunc( referencedColumns )
197{}
199
201{
202 return mAliases;
203}
204
206{
207 if ( mUsesGeometryFunc )
208 return mUsesGeometryFunc( node );
209 else
210 return mUsesGeometry;
211}
212
214{
215 mUsesGeometryFunc = usesGeometry;
216}
217
219{
220 if ( mReferencedColumnsFunc )
221 return mReferencedColumnsFunc( node );
222 else
223 return mReferencedColumns;
224}
225
227{
228 if ( mIsStaticFunc )
229 return mIsStaticFunc( node, parent, context );
230 else
231 return mIsStatic;
232}
233
235{
236 if ( mPrepareFunc )
237 return mPrepareFunc( node, parent, context );
238
239 return true;
240}
241
243{
244 mIsStaticFunc = isStatic;
245}
246
248{
249 mIsStaticFunc = nullptr;
250 mIsStatic = isStatic;
251}
252
253void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
254{
255 mPrepareFunc = prepareFunc;
256}
257
259{
260 if ( node && node->args() )
261 {
262 const QList< QgsExpressionNode * > argList = node->args()->list();
263 for ( QgsExpressionNode *argNode : argList )
264 {
265 if ( !argNode->isStatic( parent, context ) )
266 return false;
267 }
268 }
269
270 return true;
271}
272
273static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
274{
275 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
276 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
277 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
278
279 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
280 return QVariant();
281
282 QVariantList array;
283 int length = 1;
284
285 array << start;
286 double current = start + step;
287 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
288 {
289 array << current;
290 current += step;
291 length++;
292 }
293
294 return array;
295}
296
297static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
298{
299 if ( !context )
300 return QVariant();
301
302 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
303 if ( context->hasGeometry() )
304 return context->geometry();
305 else
306 {
307 FEAT_FROM_CONTEXT( context, f )
308 QgsGeometry geom = f.geometry();
309 if ( !geom.isNull() )
310 return QVariant::fromValue( geom );
311 else
312 return QVariant();
313 }
314}
315
316static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
317{
318 if ( !context )
319 return QVariant();
320
321 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
322
323 if ( name == "feature"_L1 )
324 {
325 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
326 }
327 else if ( name == "id"_L1 )
328 {
329 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
330 }
331 else if ( name == "geometry"_L1 )
332 {
333 return fcnGeometry( values, context, parent, node );
334 }
335 else
336 {
337 return context->variable( name );
338 }
339}
340
341static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
342{
343 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
344 return QgsExpression::replaceExpressionText( templateString, context );
345}
346
347static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
348{
349 if ( !context )
350 return QVariant();
351
352 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
353 QgsExpression expression( expString );
354 return expression.evaluate( context );
355}
356
357static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
358{
359 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
360 return QVariant( std::sqrt( x ) );
361}
362
363static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
364{
365 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
366 return QVariant( std::fabs( val ) );
367}
368
369static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
370{
371 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
372 return ( deg * M_PI ) / 180;
373}
374static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
375{
376 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
377 return ( 180 * rad ) / M_PI;
378}
379static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
380{
381 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
382 return QVariant( std::sin( x ) );
383}
384static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
385{
386 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
387 return QVariant( std::cos( x ) );
388}
389static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
390{
391 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
392 return QVariant( std::tan( x ) );
393}
394static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
395{
396 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
397 return QVariant( std::asin( x ) );
398}
399static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
400{
401 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
402 return QVariant( std::acos( x ) );
403}
404static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 return QVariant( std::atan( x ) );
408}
409static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
410{
411 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
412 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
413 return QVariant( std::atan2( y, x ) );
414}
415static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
416{
417 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
418 return QVariant( std::exp( x ) );
419}
420static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
421{
422 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
423 if ( x <= 0 )
424 return QVariant();
425 return QVariant( std::log( x ) );
426}
427static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
428{
429 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
430 if ( x <= 0 )
431 return QVariant();
432 return QVariant( log10( x ) );
433}
434static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
435{
436 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
437 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
438 if ( x <= 0 || b <= 0 )
439 return QVariant();
440 return QVariant( std::log( x ) / std::log( b ) );
441}
442static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
443{
444 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
445 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
446 if ( max < min )
447 return QVariant();
448
449 std::random_device rd;
450 std::mt19937_64 generator( rd() );
451
452 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
453 {
454 quint32 seed;
455 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
456 {
457 // if seed can be converted to int, we use as is
458 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
459 }
460 else
461 {
462 // if not, we hash string representation to int
463 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
464 std::hash<std::string> hasher;
465 seed = hasher( seedStr.toStdString() );
466 }
467 generator.seed( seed );
468 }
469
470 // Return a random double in the range [min, max] (inclusive)
471 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
472 return QVariant( min + f * ( max - min ) );
473}
474static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
475{
476 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
477 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
478 if ( max < min )
479 return QVariant();
480
481 std::random_device rd;
482 std::mt19937_64 generator( rd() );
483
484 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
485 {
486 quint32 seed;
487 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
488 {
489 // if seed can be converted to int, we use as is
490 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
491 }
492 else
493 {
494 // if not, we hash string representation to int
495 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
496 std::hash<std::string> hasher;
497 seed = hasher( seedStr.toStdString() );
498 }
499 generator.seed( seed );
500 }
501
502 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
503 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
504 return QVariant( randomInteger );
505
506 // Prevent wrong conversion of QVariant. See #36412
507 return QVariant( int( randomInteger ) );
508}
509
510static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
511{
512 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
513 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
514 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
515 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
516 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
517
518 if ( domainMin >= domainMax )
519 {
520 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
521 return QVariant();
522 }
523
524 // outside of domain?
525 if ( val >= domainMax )
526 {
527 return rangeMax;
528 }
529 else if ( val <= domainMin )
530 {
531 return rangeMin;
532 }
533
534 // calculate linear scale
535 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
536 double c = rangeMin - ( domainMin * m );
537
538 // Return linearly scaled value
539 return QVariant( m * val + c );
540}
541
542static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
543{
544 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
545 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
546 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
547 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
548 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
549 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
550
551 if ( domainMin >= domainMax )
552 {
553 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
554 return QVariant();
555 }
556 if ( exponent <= 0 )
557 {
558 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
559 return QVariant();
560 }
561
562 // outside of domain?
563 if ( val >= domainMax )
564 {
565 return rangeMax;
566 }
567 else if ( val <= domainMin )
568 {
569 return rangeMin;
570 }
571
572 // Return polynomially scaled value
573 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
574}
575
576static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
577{
578 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
579 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
580 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
581 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
582 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
583 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
584
585 if ( domainMin >= domainMax )
586 {
587 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
588 return QVariant();
589 }
590 if ( exponent <= 0 )
591 {
592 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
593 return QVariant();
594 }
595
596 // outside of domain?
597 if ( val >= domainMax )
598 {
599 return rangeMax;
600 }
601 else if ( val <= domainMin )
602 {
603 return rangeMin;
604 }
605
606 // Return exponentially scaled value
607 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
608 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
609}
610
611static QVariant fcnCubicBezierScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
612{
613 const double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
614 const double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
615 const double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
616 const double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
617 const double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
618
619 const double x1 = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
620 const double y1 = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
621 const double x2 = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
622 const double y2 = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
623
624 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 )
625 {
626 parent->setEvalErrorString( QObject::tr( "Cubic bezier control points must be between 0 and 1" ) );
627 return QVariant();
628 }
629
630 if ( domainMin >= domainMax )
631 {
632 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
633 return QVariant();
634 }
635
636 // outside of domain?
637 if ( val >= domainMax )
638 {
639 return rangeMax;
640 }
641 else if ( val <= domainMin )
642 {
643 return rangeMin;
644 }
645
646 // normalize input to [0, 1] range
647 const double t = ( val - domainMin ) / ( domainMax - domainMin );
648
649 // solve using UnitBezier approach (based on MapLibre native's implementation)
650 const double cx = 3.0 * x1;
651 const double bx = 3.0 * ( x2 - x1 ) - cx;
652 const double ax = 1.0 - cx - bx;
653 const double cy = 3.0 * y1;
654 const double by = 3.0 * ( y2 - y1 ) - cy;
655 const double ay = 1.0 - cy - by;
656
657 constexpr double epsilon = 1e-6;
658
659 // solve for s using Newton's method (8 iterations)
660 double s = t;
661 bool solved = false;
662 for ( int i = 0; i < 8; ++i )
663 {
664 const double x2val = ( ( ax * s + bx ) * s + cx ) * s - t;
665 if ( std::fabs( x2val ) < epsilon )
666 {
667 solved = true;
668 break;
669 }
670 const double d2 = ( 3.0 * ax * s + 2.0 * bx ) * s + cx;
671 if ( std::fabs( d2 ) < 1e-6 )
672 break;
673 s = s - x2val / d2;
674 }
675
676 if ( !solved )
677 {
678 // fallback to bisection approach
679 double t0 = 0.0;
680 double t1 = 1.0;
681 s = t;
682
683 if ( s < t0 )
684 {
685 s = t0;
686 solved = true;
687 }
688 else if ( s > t1 )
689 {
690 s = t1;
691 solved = true;
692 }
693
694 while ( !solved && t0 < t1 )
695 {
696 const double x2val = ( ( ax * s + bx ) * s + cx ) * s;
697 if ( std::fabs( x2val - t ) < epsilon )
698 {
699 solved = true;
700 break;
701 }
702 if ( t > x2val )
703 t0 = s;
704 else
705 t1 = s;
706 s = ( t1 - t0 ) * 0.5 + t0;
707 }
708 }
709
710 const double easedT = ( ( ay * s + by ) * s + cy ) * s;
711 return QVariant( ( rangeMax - rangeMin ) * easedT + rangeMin );
712}
713
714static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
715{
716 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
717 double maxVal = std::numeric_limits<double>::quiet_NaN();
718 for ( const QVariant &val : values )
719 {
720 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
721 if ( std::isnan( maxVal ) )
722 {
723 maxVal = testVal;
724 }
725 else if ( !std::isnan( testVal ) )
726 {
727 maxVal = std::max( maxVal, testVal );
728 }
729 }
730
731 if ( !std::isnan( maxVal ) )
732 {
733 result = QVariant( maxVal );
734 }
735 return result;
736}
737
738static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
739{
740 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
741 double minVal = std::numeric_limits<double>::quiet_NaN();
742 for ( const QVariant &val : values )
743 {
744 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
745 if ( std::isnan( minVal ) )
746 {
747 minVal = testVal;
748 }
749 else if ( !std::isnan( testVal ) )
750 {
751 minVal = std::min( minVal, testVal );
752 }
753 }
754
755 if ( !std::isnan( minVal ) )
756 {
757 result = QVariant( minVal );
758 }
759 return result;
760}
761
762static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
763{
764 //lazy eval, so we need to evaluate nodes now
765
766 //first node is layer id or name
767 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
769 QVariant value = node->eval( parent, context );
771
772 // TODO this expression function is NOT thread safe
774 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
776 if ( !vl )
777 {
778 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
779 return QVariant();
780 }
781
782 // second node is aggregate type
783 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
785 value = node->eval( parent, context );
787 bool ok = false;
788 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
789 if ( !ok )
790 {
791 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
792 return QVariant();
793 }
794
795 // third node is subexpression (or field name)
796 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
798 QString subExpression = node->dump();
799
801 //optional forth node is filter
802 if ( values.count() > 3 )
803 {
804 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
806 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
807 if ( !nl || nl->value().isValid() )
808 parameters.filter = node->dump();
809 }
810
811 //optional fifth node is concatenator
812 if ( values.count() > 4 )
813 {
814 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
816 value = node->eval( parent, context );
818 parameters.delimiter = value.toString();
819 }
820
821 //optional sixth node is order by
822 QString orderBy;
823 if ( values.count() > 5 )
824 {
825 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
827 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
828 if ( !nl || nl->value().isValid() )
829 {
830 orderBy = node->dump();
831 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
832 }
833 }
834
835 QString aggregateError;
836 QVariant result;
837 if ( context )
838 {
839 QString cacheKey;
840 QgsExpression subExp( subExpression );
841 QgsExpression filterExp( parameters.filter );
842
843 const QSet< QString > filterVars = filterExp.referencedVariables();
844 const QSet< QString > subExpVars = subExp.referencedVariables();
845 QSet<QString> allVars = filterVars + subExpVars;
846
847 bool isStatic = true;
848 if ( filterVars.contains( u"parent"_s ) || filterVars.contains( QString() ) || subExpVars.contains( u"parent"_s ) || subExpVars.contains( QString() ) )
849 {
850 isStatic = false;
851 }
852 else
853 {
854 for ( const QString &varName : allVars )
855 {
856 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
857 if ( scope && !scope->isStatic( varName ) )
858 {
859 isStatic = false;
860 break;
861 }
862 }
863 }
864
865 if ( isStatic && !parameters.orderBy.isEmpty() )
866 {
867 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
868 {
869 const QgsExpression &orderByExpression { orderByClause.expression() };
870 if ( orderByExpression.referencedVariables().contains( u"parent"_s ) || orderByExpression.referencedVariables().contains( QString() ) )
871 {
872 isStatic = false;
873 break;
874 }
875 }
876 }
877
878 if ( !isStatic )
879 {
880 bool ok = false;
881 const QString contextHash = context->uniqueHash( ok, allVars );
882 if ( ok )
883 {
884 cacheKey = u"aggfcn:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
885 }
886 }
887 else
888 {
889 cacheKey = u"aggfcn:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
890 }
891
892 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
893 {
894 return context->cachedValue( cacheKey );
895 }
896
897 QgsExpressionContext subContext( *context );
899 subScope->setVariable( u"parent"_s, context->feature(), true );
900 subContext.appendScope( subScope );
901 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
902
903 if ( ok && !cacheKey.isEmpty() )
904 {
905 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
906 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
907 // associated with it's calculation!
908 context->setCachedValue( cacheKey, result );
909 }
910 }
911 else
912 {
913 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
914 }
915 if ( !ok )
916 {
917 if ( !aggregateError.isEmpty() )
918 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
919 else
920 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
921 return QVariant();
922 }
923
924 return result;
925}
926
927static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
928{
929 if ( !context )
930 {
931 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
932 return QVariant();
933 }
934
935 // first step - find current layer
936
937 // TODO this expression function is NOT thread safe
939 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
941 if ( !vl )
942 {
943 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
944 return QVariant();
945 }
946
947 //lazy eval, so we need to evaluate nodes now
948
949 //first node is relation name
950 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
952 QVariant value = node->eval( parent, context );
954 QString relationId = value.toString();
955 // check relation exists
956 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
957 if ( !relation.isValid() || relation.referencedLayer() != vl )
958 {
959 // check for relations by name
960 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
961 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
962 {
963 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
964 return QVariant();
965 }
966 else
967 {
968 relation = relations.at( 0 );
969 }
970 }
971
972 QgsVectorLayer *childLayer = relation.referencingLayer();
973
974 // second node is aggregate type
975 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
977 value = node->eval( parent, context );
979 bool ok = false;
980 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
981 if ( !ok )
982 {
983 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
984 return QVariant();
985 }
986
987 //third node is subexpression (or field name)
988 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
990 QString subExpression = node->dump();
991
992 //optional fourth node is concatenator
994 if ( values.count() > 3 )
995 {
996 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
998 value = node->eval( parent, context );
1000 parameters.delimiter = value.toString();
1001 }
1002
1003 //optional fifth node is order by
1004 QString orderBy;
1005 if ( values.count() > 4 )
1006 {
1007 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
1009 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1010 if ( !nl || nl->value().isValid() )
1011 {
1012 orderBy = node->dump();
1013 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1014 }
1015 }
1016
1017 if ( !context->hasFeature() )
1018 return QVariant();
1019 QgsFeature f = context->feature();
1020
1021 parameters.filter = relation.getRelatedFeaturesFilter( f );
1022
1023 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 );
1024 if ( context->hasCachedValue( cacheKey ) )
1025 return context->cachedValue( cacheKey );
1026
1027 QVariant result;
1028 ok = false;
1029
1030
1031 QgsExpressionContext subContext( *context );
1032 QString error;
1033 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1034
1035 if ( !ok )
1036 {
1037 if ( !error.isEmpty() )
1038 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1039 else
1040 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1041 return QVariant();
1042 }
1043
1044 // cache value
1045 context->setCachedValue( cacheKey, result );
1046 return result;
1047}
1048
1049
1050static QVariant fcnAggregateGeneric(
1051 Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1
1052)
1053{
1054 if ( !context )
1055 {
1056 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1057 return QVariant();
1058 }
1059
1060 // first step - find current layer
1061
1062 // TODO this expression function is NOT thread safe
1064 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
1066 if ( !vl )
1067 {
1068 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1069 return QVariant();
1070 }
1071
1072 //lazy eval, so we need to evaluate nodes now
1073
1074 //first node is subexpression (or field name)
1075 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1077 QString subExpression = node->dump();
1078
1079 //optional second node is group by
1080 QString groupBy;
1081 if ( values.count() > 1 )
1082 {
1083 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
1085 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1086 if ( !nl || nl->value().isValid() )
1087 groupBy = node->dump();
1088 }
1089
1090 //optional third node is filter
1091 if ( values.count() > 2 )
1092 {
1093 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
1095 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1096 if ( !nl || nl->value().isValid() )
1097 parameters.filter = node->dump();
1098 }
1099
1100 //optional order by node, if supported
1101 QString orderBy;
1102 if ( orderByPos >= 0 && values.count() > orderByPos )
1103 {
1104 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
1106 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1107 if ( !nl || nl->value().isValid() )
1108 {
1109 orderBy = node->dump();
1110 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1111 }
1112 }
1113
1114 // build up filter with group by
1115
1116 // find current group by value
1117 if ( !groupBy.isEmpty() )
1118 {
1119 QgsExpression groupByExp( groupBy );
1120 QVariant groupByValue = groupByExp.evaluate( context );
1121 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy, QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s, QgsExpression::quotedValue( groupByValue ) );
1122 if ( !parameters.filter.isEmpty() )
1123 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1124 else
1125 parameters.filter = groupByClause;
1126 }
1127
1128 QgsExpression subExp( subExpression );
1129 QgsExpression filterExp( parameters.filter );
1130
1131 bool isStatic = true;
1132 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1133 for ( const QString &varName : refVars )
1134 {
1135 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1136 if ( scope && !scope->isStatic( varName ) )
1137 {
1138 isStatic = false;
1139 break;
1140 }
1141 }
1142
1143 QString cacheKey;
1144 if ( !isStatic )
1145 {
1146 bool ok = false;
1147 const QString contextHash = context->uniqueHash( ok, refVars );
1148 if ( ok )
1149 {
1150 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
1151 }
1152 }
1153 else
1154 {
1155 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1156 }
1157
1158 if ( context->hasCachedValue( cacheKey ) )
1159 return context->cachedValue( cacheKey );
1160
1161 QVariant result;
1162 bool ok = false;
1163
1164 QgsExpressionContext subContext( *context );
1166 subScope->setVariable( u"parent"_s, context->feature(), true );
1167 subContext.appendScope( subScope );
1168 QString error;
1169 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1170
1171 if ( !ok )
1172 {
1173 if ( !error.isEmpty() )
1174 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1175 else
1176 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1177 return QVariant();
1178 }
1179
1180 // cache value
1181 context->setCachedValue( cacheKey, result );
1182 return result;
1183}
1184
1185
1186static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1187{
1188 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1189}
1190
1191static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1192{
1193 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1194}
1195
1196static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1197{
1198 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1199}
1200
1201static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1202{
1203 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1204}
1205
1206static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1207{
1208 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1209}
1210
1211static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1212{
1213 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1214}
1215
1216static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1217{
1218 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1219}
1220
1221static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1222{
1223 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1224}
1225
1226static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1227{
1228 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1229}
1230
1231static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1232{
1233 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1234}
1235
1236static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1237{
1238 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1239}
1240
1241static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1244}
1245
1246static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1247{
1248 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1249}
1250
1251static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1252{
1253 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1254}
1255
1256static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1257{
1258 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1259}
1260
1261static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1262{
1263 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1264}
1265
1266static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1267{
1268 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1269}
1270
1271static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1272{
1273 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1274}
1275
1276static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1277{
1279
1280 //fourth node is concatenator
1281 if ( values.count() > 3 )
1282 {
1283 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1285 QVariant value = node->eval( parent, context );
1287 parameters.delimiter = value.toString();
1288 }
1289
1290 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1291}
1292
1293static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1294{
1296
1297 //fourth node is concatenator
1298 if ( values.count() > 3 )
1299 {
1300 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1302 QVariant value = node->eval( parent, context );
1304 parameters.delimiter = value.toString();
1305 }
1306
1307 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1308}
1309
1310static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1311{
1312 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1313}
1314
1315static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1316{
1317 if ( !context )
1318 return QVariant();
1319
1320 QVariant scale = context->variable( u"map_scale"_s );
1321 bool ok = false;
1322 if ( QgsVariantUtils::isNull( scale ) )
1323 return QVariant();
1324
1325 const double v = scale.toDouble( &ok );
1326 if ( ok )
1327 return v;
1328 return QVariant();
1329}
1330
1331static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1332{
1333 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1334 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1335 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1336
1337 // force testValue to sit inside the range specified by the min and max value
1338 if ( testValue <= minValue )
1339 {
1340 return QVariant( minValue );
1341 }
1342 else if ( testValue >= maxValue )
1343 {
1344 return QVariant( maxValue );
1345 }
1346 else
1347 {
1348 return QVariant( testValue );
1349 }
1350}
1351
1352static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1353{
1354 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1355 return QVariant( std::floor( x ) );
1356}
1357
1358static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1359{
1360 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1361 return QVariant( std::ceil( x ) );
1362}
1363
1364static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1365{
1366 const QVariant value = values.at( 0 );
1367 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1368 {
1369 return QVariant( false );
1370 }
1371 else if ( value.userType() == QMetaType::QString )
1372 {
1373 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1374 return QVariant( !value.toString().isEmpty() );
1375 }
1376 else if ( QgsExpressionUtils::isList( value ) )
1377 {
1378 return !value.toList().isEmpty();
1379 }
1380 return QVariant( value.toBool() );
1381}
1382static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1383{
1384 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1385}
1386static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1387{
1388 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1389}
1390static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1391{
1392 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1393}
1394
1395static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1396{
1397 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1398 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1399 if ( format.isEmpty() && !language.isEmpty() )
1400 {
1401 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1402 return QVariant( QDateTime() );
1403 }
1404
1405 if ( format.isEmpty() && language.isEmpty() )
1406 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1407
1408 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409 QLocale locale = QLocale();
1410 if ( !language.isEmpty() )
1411 {
1412 locale = QLocale( language );
1413 }
1414
1415 QDateTime datetime = locale.toDateTime( datetimestring, format );
1416 if ( !datetime.isValid() )
1417 {
1418 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1419 datetime = QDateTime();
1420 }
1421 return QVariant( datetime );
1422}
1423
1424static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1425{
1426 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1427 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1428 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1429
1430 const QDate date( year, month, day );
1431 if ( !date.isValid() )
1432 {
1433 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1434 return QVariant();
1435 }
1436 return QVariant( date );
1437}
1438
1439static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1440{
1441 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1442 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1443 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1444
1445 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1446 if ( !time.isValid() )
1447 {
1448 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1449 return QVariant();
1450 }
1451 return QVariant( time );
1452}
1453
1454static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1455{
1456 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1457 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1458 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1459 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1460 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1461 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1462
1463 const QDate date( year, month, day );
1464 if ( !date.isValid() )
1465 {
1466 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1467 return QVariant();
1468 }
1469 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1470 if ( !time.isValid() )
1471 {
1472 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1473 return QVariant();
1474 }
1475 return QVariant( QDateTime( date, time ) );
1476}
1477
1478static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1479{
1480 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1481
1482 QTimeZone tz;
1483
1484#if QT_FEATURE_timezone > 0
1485 if ( !timeZoneId.isEmpty() )
1486 {
1487 tz = QTimeZone( timeZoneId.toUtf8() );
1488 }
1489
1490 if ( !tz.isValid() )
1491 {
1492 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1493 return QVariant();
1494 }
1495
1496#else
1497 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1498#endif
1499 return QVariant::fromValue( tz );
1500}
1501
1502static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1503{
1504#if QT_FEATURE_timezone > 0
1505 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1506 if ( datetime.isValid() )
1507 {
1508 return QVariant::fromValue( datetime.timeZone() );
1509 }
1510 return QVariant();
1511#else
1512 Q_UNUSED( values )
1513 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1514 return QVariant();
1515#endif
1516}
1517
1518static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1519{
1520#if QT_FEATURE_timezone > 0
1521 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1522 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1523 if ( datetime.isValid() && tz.isValid() )
1524 {
1525 datetime.setTimeZone( tz );
1526 return QVariant::fromValue( datetime );
1527 }
1528 return QVariant();
1529#else
1530 Q_UNUSED( values )
1531 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1532 return QVariant();
1533#endif
1534}
1535
1536static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1537{
1538#if QT_FEATURE_timezone > 0
1539 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1540 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1541 if ( datetime.isValid() && tz.isValid() )
1542 {
1543 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1544 }
1545 return QVariant();
1546#else
1547 Q_UNUSED( values )
1548 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1549 return QVariant();
1550#endif
1551}
1552
1553static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1554{
1555#if QT_FEATURE_timezone > 0
1556 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1557 if ( timeZone.isValid() )
1558 {
1559 return QString( timeZone.id() );
1560 }
1561 return QVariant();
1562#else
1563 Q_UNUSED( values )
1564 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1565 return QVariant();
1566#endif
1567}
1568
1569static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1570{
1571 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1572 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1573 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1574 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1575 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1576 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1577 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1578
1579 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1580}
1581
1582static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1583{
1584 for ( const QVariant &value : values )
1585 {
1586 if ( QgsVariantUtils::isNull( value ) )
1587 continue;
1588 return value;
1589 }
1590 return QVariant();
1591}
1592
1593static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1594{
1595 const QVariant val1 = values.at( 0 );
1596 const QVariant val2 = values.at( 1 );
1597
1598 if ( val1 == val2 )
1599 return QVariant();
1600 else
1601 return val1;
1602}
1603
1604static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1605{
1606 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1607 return QVariant( str.toLower() );
1608}
1609static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1610{
1611 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1612 return QVariant( str.toUpper() );
1613}
1614static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1615{
1616 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1617 QStringList elems = str.split( ' ' );
1618 for ( int i = 0; i < elems.size(); i++ )
1619 {
1620 if ( elems[i].size() > 1 )
1621 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1622 }
1623 return QVariant( elems.join( ' '_L1 ) );
1624}
1625
1626static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1627{
1628 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1629 return QVariant( str.trimmed() );
1630}
1631
1632static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1633{
1634 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1635
1636 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1637
1638 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1639 str.replace( re, QString() );
1640 return QVariant( str );
1641}
1642
1643static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1644{
1645 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1646
1647 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1648
1649 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1650 str.replace( re, QString() );
1651 return QVariant( str );
1652}
1653
1654static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1655{
1656 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1657 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1658 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1659}
1660
1661static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1662{
1663 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1664 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1665 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1666}
1667
1668static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1669{
1670 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1671 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1672 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1673 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1674}
1675
1676static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1677{
1678 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1679 return QVariant( QgsStringUtils::soundex( string ) );
1680}
1681
1682static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1683{
1684 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1685 return QVariant( QString( character ) );
1686}
1687
1688static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689{
1690 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1691
1692 if ( value.isEmpty() )
1693 {
1694 return QVariant();
1695 }
1696
1697 int res = value.at( 0 ).unicode();
1698 return QVariant( res );
1699}
1700
1701static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1702{
1703 if ( values.length() == 2 || values.length() == 3 )
1704 {
1705 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1706 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1707
1708 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1709
1710 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1711 }
1712
1713 return QVariant();
1714}
1715
1716static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1717{
1718 // two variants, one for geometry, one for string
1719
1720 //geometry variant
1721 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1722 if ( !geom.isNull() )
1723 {
1724 if ( geom.type() == Qgis::GeometryType::Line )
1725 return QVariant( geom.length() );
1726 else
1727 return QVariant();
1728 }
1729
1730 //otherwise fall back to string variant
1731 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1732 return QVariant( str.length() );
1733}
1734
1735static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1736{
1737 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1738
1739 if ( geom.type() != Qgis::GeometryType::Line )
1740 return QVariant();
1741
1742 double totalLength = 0;
1743 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1744 {
1746 {
1747 totalLength += line->length3D();
1748 }
1749 else
1750 {
1751 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1752 totalLength += segmentized->length3D();
1753 }
1754 }
1755
1756 return totalLength;
1757}
1758
1759
1760static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1761{
1762 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1763 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1764 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1765}
1766
1767static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1768{
1769 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1770 {
1771 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1772 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1773 QVector< QPair< QString, QString > > mapItems;
1774
1775 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1776 {
1777 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1778 }
1779
1780 // larger keys should be replaced first since they may contain whole smaller keys
1781 std::sort( mapItems.begin(), mapItems.end(), []( const QPair< QString, QString > &pair1, const QPair< QString, QString > &pair2 ) { return ( pair1.first.length() > pair2.first.length() ); } );
1782
1783 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1784 {
1785 str = str.replace( it->first, it->second );
1786 }
1787
1788 return QVariant( str );
1789 }
1790 else if ( values.count() == 3 )
1791 {
1792 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1793 QVariantList before;
1794 QVariantList after;
1795 bool isSingleReplacement = false;
1796
1797 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1798 {
1799 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1800 }
1801 else
1802 {
1803 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1804 }
1805
1806 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1807 {
1808 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1809 isSingleReplacement = true;
1810 }
1811 else
1812 {
1813 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1814 }
1815
1816 if ( !isSingleReplacement && before.length() != after.length() )
1817 {
1818 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1819 return QVariant();
1820 }
1821
1822 for ( int i = 0; i < before.length(); i++ )
1823 {
1824 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1825 }
1826
1827 return QVariant( str );
1828 }
1829 else
1830 {
1831 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1832 return QVariant();
1833 }
1834}
1835
1836static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1837{
1838 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1839 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1840 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1841
1842 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1843 if ( !re.isValid() )
1844 {
1845 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1846 return QVariant();
1847 }
1848 return QVariant( str.replace( re, after ) );
1849}
1850
1851static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1852{
1853 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1854 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1855
1856 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1857 if ( !re.isValid() )
1858 {
1859 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1860 return QVariant();
1861 }
1862 return QVariant( ( str.indexOf( re ) + 1 ) );
1863}
1864
1865static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1866{
1867 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1868 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1869 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1870
1871 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1872 if ( !re.isValid() )
1873 {
1874 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1875 return QVariant();
1876 }
1877
1878 QRegularExpressionMatch matches = re.match( str );
1879 if ( matches.hasMatch() )
1880 {
1881 QVariantList array;
1882 QStringList list = matches.capturedTexts();
1883
1884 // Skip the first string to only return captured groups
1885 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1886 {
1887 array += ( !( *it ).isEmpty() ) ? *it : empty;
1888 }
1889
1890 return QVariant( array );
1891 }
1892 else
1893 {
1894 return QVariant();
1895 }
1896}
1897
1898static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1899{
1900 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1901 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1902
1903 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1904 if ( !re.isValid() )
1905 {
1906 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1907 return QVariant();
1908 }
1909
1910 // extract substring
1911 QRegularExpressionMatch match = re.match( str );
1912 if ( match.hasMatch() )
1913 {
1914 // return first capture
1915 if ( match.lastCapturedIndex() > 0 )
1916 {
1917 // a capture group was present, so use that
1918 return QVariant( match.captured( 1 ) );
1919 }
1920 else
1921 {
1922 // no capture group, so using all match
1923 return QVariant( match.captured( 0 ) );
1924 }
1925 }
1926 else
1927 {
1928 return QVariant( "" );
1929 }
1930}
1931
1932static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1933{
1934 QString uuid = QUuid::createUuid().toString();
1935 if ( values.at( 0 ).toString().compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1936 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1937 else if ( values.at( 0 ).toString().compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1938 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1939 return uuid;
1940}
1941
1942static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1943{
1944 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1945 return QVariant();
1946
1947 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1948 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1949
1950 int len = 0;
1951 if ( values.at( 2 ).isValid() )
1952 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1953 else
1954 len = str.size();
1955
1956 if ( from < 0 )
1957 {
1958 from = str.size() + from;
1959 if ( from < 0 )
1960 {
1961 from = 0;
1962 }
1963 }
1964 else if ( from > 0 )
1965 {
1966 //account for the fact that substr() starts at 1
1967 from -= 1;
1968 }
1969
1970 if ( len < 0 )
1971 {
1972 len = str.size() + len - from;
1973 if ( len < 0 )
1974 {
1975 len = 0;
1976 }
1977 }
1978
1979 return QVariant( str.mid( from, len ) );
1980}
1981static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1982{
1983 FEAT_FROM_CONTEXT( context, f )
1984 return QVariant( f.id() );
1985}
1986
1987static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1988{
1989 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1990 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1991 bool foundLayer = false;
1992 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
1993 values.at( 0 ),
1994 context,
1995 parent,
1996 [parent, bandNb, geom]( QgsMapLayer *mapLayer ) {
1997 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1998 if ( !layer || !layer->dataProvider() )
1999 {
2000 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2001 return QVariant();
2002 }
2003
2004 if ( bandNb < 1 || bandNb > layer->bandCount() )
2005 {
2006 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
2007 return QVariant();
2008 }
2009
2010 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
2011 {
2012 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
2013 return QVariant();
2014 }
2015
2016 QgsPointXY point = geom.asPoint();
2017 if ( geom.isMultipart() )
2018 {
2019 QgsMultiPointXY multiPoint = geom.asMultiPoint();
2020 if ( multiPoint.count() == 1 )
2021 {
2022 point = multiPoint[0];
2023 }
2024 else
2025 {
2026 // if the geometry contains more than one part, return an undefined value
2027 return QVariant();
2028 }
2029 }
2030
2031 double value = layer->dataProvider()->sample( point, bandNb );
2032 return std::isnan( value ) ? QVariant() : value;
2033 },
2034 foundLayer
2035 );
2036
2037 if ( !foundLayer )
2038 {
2039 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2040 return QVariant();
2041 }
2042 else
2043 {
2044 return res;
2045 }
2046}
2047
2048static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2049{
2050 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2051 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2052
2053 bool foundLayer = false;
2054 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2055 values.at( 0 ),
2056 context,
2057 parent,
2058 [parent, bandNb, value]( QgsMapLayer *mapLayer ) -> QVariant {
2059 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
2060 if ( !layer || !layer->dataProvider() )
2061 {
2062 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2063 return QVariant();
2064 }
2065
2066 if ( bandNb < 1 || bandNb > layer->bandCount() )
2067 {
2068 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
2069 return QVariant();
2070 }
2071
2072 if ( std::isnan( value ) )
2073 {
2074 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
2075 return QVariant();
2076 }
2077
2078 if ( !layer->dataProvider()->attributeTable( bandNb ) )
2079 {
2080 return QVariant();
2081 }
2082
2083 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
2084 if ( data.isEmpty() )
2085 {
2086 return QVariant();
2087 }
2088
2089 QVariantMap result;
2090 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
2091 for ( int idx = 0; idx < static_cast<int>( fields.count() ) && idx < static_cast<int>( data.count() ); ++idx )
2092 {
2093 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
2094 if ( field.isColor() || field.isRamp() )
2095 {
2096 continue;
2097 }
2098 result.insert( fields.at( idx ).name, data.at( idx ) );
2099 }
2100
2101 return result;
2102 },
2103 foundLayer
2104 );
2105
2106 if ( !foundLayer )
2107 {
2108 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2109 return QVariant();
2110 }
2111 else
2112 {
2113 return res;
2114 }
2115}
2116
2117static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2118{
2119 if ( !context )
2120 return QVariant();
2121
2122 return context->feature();
2123}
2124
2125static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2126{
2127 QgsFeature feature;
2128 QString attr;
2129 if ( values.size() == 1 )
2130 {
2131 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2132 feature = context->feature();
2133 }
2134 else if ( values.size() == 2 )
2135 {
2136 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2137 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2138 }
2139 else
2140 {
2141 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2142 return QVariant();
2143 }
2144
2145 return feature.attribute( attr );
2146}
2147
2148static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2149{
2150 QString table { R"html(
2151 <table>
2152 <thead>
2153 <tr><th>%1</th></tr>
2154 </thead>
2155 <tbody>
2156 <tr><td>%2</td></tr>
2157 </tbody>
2158 </table>)html" };
2159 QVariantMap dict;
2160 if ( values.size() == 1 )
2161 {
2162 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2163 }
2164 else
2165 {
2166 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2167 return QVariant();
2168 }
2169
2170 if ( dict.isEmpty() )
2171 {
2172 return QVariant();
2173 }
2174
2175 QStringList headers;
2176 QStringList cells;
2177
2178 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2179 {
2180 headers.push_back( it.key().toHtmlEscaped() );
2181 cells.push_back( it.value().toString().toHtmlEscaped() );
2182 }
2183
2184 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2185}
2186
2187static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2188{
2189 QString table { R"html(
2190 <dl>
2191 %1
2192 </dl>)html" };
2193 QVariantMap dict;
2194 if ( values.size() == 1 )
2195 {
2196 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2197 }
2198 else
2199 {
2200 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2201 return QVariant();
2202 }
2203
2204 if ( dict.isEmpty() )
2205 {
2206 return QVariant();
2207 }
2208
2209 QString rows;
2210
2211 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2212 {
2213 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2214 }
2215
2216 return table.arg( rows );
2217}
2218
2219static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2220{
2221 QVariant layer;
2222 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2223 {
2224 layer = context->variable( u"layer"_s );
2225 }
2226 else
2227 {
2228 //first node is layer id or name
2229 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2231 layer = node->eval( parent, context );
2233 }
2234
2235 QgsFeature feature;
2236 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2237 {
2238 feature = context->feature();
2239 }
2240 else
2241 {
2242 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2243 }
2244
2246 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2247 if ( strength == "hard"_L1 )
2248 {
2250 }
2251 else if ( strength == "soft"_L1 )
2252 {
2254 }
2255
2256 bool foundLayer = false;
2257 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2258 layer,
2259 context,
2260 parent,
2261 [parent, feature, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2262 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2263 if ( !layer )
2264 {
2265 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2266 return QVariant();
2267 }
2268
2269 const QgsFields fields = layer->fields();
2270 bool valid = true;
2271 for ( int i = 0; i < fields.size(); i++ )
2272 {
2273 QStringList errors;
2274 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2275 if ( !valid )
2276 {
2277 break;
2278 }
2279 }
2280
2281 return valid;
2282 },
2283 foundLayer
2284 );
2285
2286 if ( !foundLayer )
2287 {
2288 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2289 return QVariant();
2290 }
2291
2292 return res;
2293}
2294
2295static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2296{
2297 QVariant layer;
2298 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2299 {
2300 layer = context->variable( u"layer"_s );
2301 }
2302 else
2303 {
2304 //first node is layer id or name
2305 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2307 layer = node->eval( parent, context );
2309 }
2310
2311 QgsFeature feature;
2312 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2313 {
2314 feature = context->feature();
2315 }
2316 else
2317 {
2318 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2319 }
2320
2322 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2323 if ( strength == "hard"_L1 )
2324 {
2326 }
2327 else if ( strength == "soft"_L1 )
2328 {
2330 }
2331
2332 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2333
2334 bool foundLayer = false;
2335 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2336 layer,
2337 context,
2338 parent,
2339 [parent, feature, attributeName, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2340 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2341 if ( !layer )
2342 {
2343 return QVariant();
2344 }
2345
2346 const int fieldIndex = layer->fields().indexFromName( attributeName );
2347 if ( fieldIndex == -1 )
2348 {
2349 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2350 return QVariant();
2351 }
2352
2353 QStringList errors;
2354 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2355 return valid;
2356 },
2357 foundLayer
2358 );
2359
2360 if ( !foundLayer )
2361 {
2362 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2363 return QVariant();
2364 }
2365
2366 return res;
2367}
2368
2369static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2370{
2371 QgsFeature feature;
2372 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2373 {
2374 feature = context->feature();
2375 }
2376 else
2377 {
2378 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2379 }
2380
2381 const QgsFields fields = feature.fields();
2382 QVariantMap result;
2383 for ( int i = 0; i < fields.count(); ++i )
2384 {
2385 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2386 }
2387 return result;
2388}
2389
2390static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2391{
2392 QgsVectorLayer *layer = nullptr;
2393 QgsFeature feature;
2394
2395 // TODO this expression function is NOT thread safe
2397 if ( values.isEmpty() )
2398 {
2399 feature = context->feature();
2400 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2401 }
2402 else if ( values.size() == 1 )
2403 {
2404 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2405 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2406 }
2407 else if ( values.size() == 2 )
2408 {
2409 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2410 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2411 }
2412 else
2413 {
2414 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2415 return QVariant();
2416 }
2418
2419 if ( !layer )
2420 {
2421 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2422 return QVariant();
2423 }
2424
2425 if ( !feature.isValid() )
2426 {
2427 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2428 return QVariant();
2429 }
2430
2431 const QgsFields fields = feature.fields();
2432 QVariantMap result;
2433 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2434 {
2435 const QString fieldName { fields.at( fieldIndex ).name() };
2436 const QVariant attributeVal = feature.attribute( fieldIndex );
2437 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2438 if ( context && context->hasCachedValue( cacheValueKey ) )
2439 {
2440 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2441 }
2442 else
2443 {
2444 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2446 QVariant cache;
2447 if ( context )
2448 {
2449 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2450
2451 if ( !context->hasCachedValue( cacheKey ) )
2452 {
2453 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2454 context->setCachedValue( cacheKey, cache );
2455 }
2456 else
2457 {
2458 cache = context->cachedValue( cacheKey );
2459 }
2460 }
2461 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2462
2463 result.insert( fields.at( fieldIndex ).name(), value );
2464
2465 if ( context )
2466 {
2467 context->setCachedValue( cacheValueKey, value );
2468 }
2469 }
2470 }
2471 return result;
2472}
2473
2474static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2475{
2476 QgsVectorLayer *layer = nullptr;
2477 QgsFeature feature;
2478 bool evaluate = true;
2479
2480 // TODO this expression function is NOT thread safe
2482 if ( values.isEmpty() )
2483 {
2484 feature = context->feature();
2485 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2486 }
2487 else if ( values.size() == 1 )
2488 {
2489 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2490 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2491 }
2492 else if ( values.size() == 2 )
2493 {
2494 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2495 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2496 }
2497 else if ( values.size() == 3 )
2498 {
2499 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2500 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2501 evaluate = values.value( 2 ).toBool();
2502 }
2503 else
2504 {
2505 if ( isMaptip )
2506 {
2507 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2508 }
2509 else
2510 {
2511 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2512 }
2513 return QVariant();
2514 }
2515
2516 if ( !layer )
2517 {
2518 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2519 return QVariant();
2520 }
2522
2523 if ( !feature.isValid() )
2524 {
2525 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2526 return QVariant();
2527 }
2528
2529 if ( !evaluate )
2530 {
2531 if ( isMaptip )
2532 {
2533 return layer->mapTipTemplate();
2534 }
2535 else
2536 {
2537 return layer->displayExpression();
2538 }
2539 }
2540
2541 QgsExpressionContext subContext( *context );
2542 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2543 subContext.setFeature( feature );
2544
2545 if ( isMaptip )
2546 {
2547 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2548 }
2549 else
2550 {
2551 QgsExpression exp( layer->displayExpression() );
2552 exp.prepare( &subContext );
2553 return exp.evaluate( &subContext ).toString();
2554 }
2555}
2556
2557static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2558{
2559 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2560}
2561
2562static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2563{
2564 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2565}
2566
2567static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2568{
2569 QgsFeature feature;
2570 QVariant layer;
2571 if ( values.isEmpty() )
2572 {
2573 feature = context->feature();
2574 layer = context->variable( u"layer"_s );
2575 }
2576 else if ( values.size() == 1 )
2577 {
2578 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2579 layer = context->variable( u"layer"_s );
2580 }
2581 else if ( values.size() == 2 )
2582 {
2583 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2584 layer = values.at( 0 );
2585 }
2586 else
2587 {
2588 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2589 return QVariant();
2590 }
2591
2592 bool foundLayer = false;
2593 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2594 layer,
2595 context,
2596 parent,
2597 [feature]( QgsMapLayer *mapLayer ) -> QVariant {
2598 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2599 if ( !layer || !feature.isValid() )
2600 {
2601 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2602 }
2603
2604 return layer->selectedFeatureIds().contains( feature.id() );
2605 },
2606 foundLayer
2607 );
2608 if ( !foundLayer )
2609 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2610 else
2611 return res;
2612}
2613
2614static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2615{
2616 QVariant layer;
2617
2618 if ( values.isEmpty() )
2619 layer = context->variable( u"layer"_s );
2620 else if ( values.count() == 1 )
2621 layer = values.at( 0 );
2622 else
2623 {
2624 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2625 return QVariant();
2626 }
2627
2628 bool foundLayer = false;
2629 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2630 layer,
2631 context,
2632 parent,
2633 []( QgsMapLayer *mapLayer ) -> QVariant {
2634 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2635 if ( !layer )
2636 {
2637 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2638 }
2639
2640 return layer->selectedFeatureCount();
2641 },
2642 foundLayer
2643 );
2644 if ( !foundLayer )
2645 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2646 else
2647 return res;
2648}
2649
2650static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2651{
2652 static QMap<QString, qlonglong> counterCache;
2653 QVariant functionResult;
2654
2655 auto fetchAndIncrementFunc = [values, parent, &functionResult]( QgsMapLayer *mapLayer, const QString &databaseArgument ) {
2656 QString database;
2657
2658 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2659
2660 if ( layer )
2661 {
2662 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2663 database = decodedUri.value( u"path"_s ).toString();
2664 if ( database.isEmpty() )
2665 {
2666 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2667 }
2668 }
2669 else
2670 {
2671 database = databaseArgument;
2672 }
2673
2674 const QString table = values.at( 1 ).toString();
2675 const QString idColumn = values.at( 2 ).toString();
2676 const QString filterAttribute = values.at( 3 ).toString();
2677 const QVariant filterValue = values.at( 4 ).toString();
2678 const QVariantMap defaultValues = values.at( 5 ).toMap();
2679
2680 // read from database
2682 sqlite3_statement_unique_ptr sqliteStatement;
2683
2684 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2685 {
2686 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2687 functionResult = QVariant();
2688 return;
2689 }
2690
2691 QString errorMessage;
2692 QString currentValSql;
2693
2694 qlonglong nextId = 0;
2695 bool cachedMode = false;
2696 bool valueRetrieved = false;
2697
2698 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2699
2700 // Running in transaction mode, check for cached value first
2701 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2702 {
2703 cachedMode = true;
2704
2705 auto cachedCounter = counterCache.find( cacheString );
2706
2707 if ( cachedCounter != counterCache.end() )
2708 {
2709 qlonglong &cachedValue = cachedCounter.value();
2710 nextId = cachedValue;
2711 nextId += 1;
2712 cachedValue = nextId;
2713 valueRetrieved = true;
2714 }
2715 }
2716
2717 // Either not in cached mode or no cached value found, obtain from DB
2718 if ( !cachedMode || !valueRetrieved )
2719 {
2720 int result = SQLITE_ERROR;
2721
2722 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2723 if ( !filterAttribute.isNull() )
2724 {
2725 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2726 }
2727
2728 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2729
2730 if ( result == SQLITE_OK )
2731 {
2732 nextId = 0;
2733 if ( sqliteStatement.step() == SQLITE_ROW )
2734 {
2735 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2736 }
2737
2738 // If in cached mode: add value to cache and connect to transaction
2739 if ( cachedMode && result == SQLITE_OK )
2740 {
2741 counterCache.insert( cacheString, nextId );
2742
2743 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() { counterCache.remove( cacheString ); } );
2744 }
2745 valueRetrieved = true;
2746 }
2747 }
2748
2749 if ( valueRetrieved )
2750 {
2751 QString upsertSql;
2752 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2753 QStringList cols;
2754 QStringList vals;
2755 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2756 vals << QgsSqliteUtils::quotedValue( nextId );
2757
2758 if ( !filterAttribute.isNull() )
2759 {
2760 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2761 vals << QgsSqliteUtils::quotedValue( filterValue );
2762 }
2763
2764 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2765 {
2766 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2767 vals << iter.value().toString();
2768 }
2769
2770 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2771 upsertSql += " VALUES "_L1;
2772 upsertSql += '(' + vals.join( ',' ) + ')';
2773
2774 int result = SQLITE_ERROR;
2775 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2776 {
2777 QgsTransaction *transaction = layer->dataProvider()->transaction();
2778 if ( transaction->executeSql( upsertSql, errorMessage ) )
2779 {
2780 result = SQLITE_OK;
2781 }
2782 }
2783 else
2784 {
2785 result = sqliteDb.exec( upsertSql, errorMessage );
2786 }
2787 if ( result == SQLITE_OK )
2788 {
2789 functionResult = QVariant( nextId );
2790 return;
2791 }
2792 else
2793 {
2794 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2795 functionResult = QVariant();
2796 return;
2797 }
2798 }
2799
2800 functionResult = QVariant();
2801 };
2802
2803 bool foundLayer = false;
2804 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer *layer ) { fetchAndIncrementFunc( layer, QString() ); }, foundLayer );
2805 if ( !foundLayer )
2806 {
2807 const QString databasePath = values.at( 0 ).toString();
2808 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath] { fetchAndIncrementFunc( nullptr, databasePath ); } );
2809 }
2810
2811 return functionResult;
2812}
2813
2814static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2815{
2816 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2817 if ( !crs.isValid() )
2818 return QVariant();
2819 return crs.authid();
2820}
2821
2822static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2823{
2824 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2825 QgsCoordinateReferenceSystem crs( definition );
2826
2827 if ( !crs.isValid() )
2828 {
2829 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2830 }
2831
2832 return QVariant::fromValue( crs );
2833}
2834
2835static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2836{
2837 QString concat;
2838 for ( const QVariant &value : values )
2839 {
2840 if ( !QgsVariantUtils::isNull( value ) )
2841 concat += QgsExpressionUtils::getStringValue( value, parent );
2842 }
2843 return concat;
2844}
2845
2846static QVariant fcnConcatWs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2847{
2848 if ( values.length() < 2 )
2849 {
2850 parent->setEvalErrorString( QObject::tr( "Function concat_ws requires at least 2 arguments" ) );
2851 return QVariant();
2852 }
2853
2854 const QString separator = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2855
2856 QStringList stringValues;
2857 stringValues.reserve( values.size() - 1 );
2858 for ( int i = 1; i < values.size(); ++i )
2859 {
2860 const QVariant value = values.at( i );
2861 if ( !QgsVariantUtils::isNull( value ) )
2862 {
2863 stringValues.append( QgsExpressionUtils::getStringValue( value, parent ) );
2864 }
2865 }
2866
2867 return stringValues.join( separator );
2868}
2869
2870static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2871{
2872 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2873 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2874}
2875
2876static QVariant fcnUnaccent( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction *node )
2877{
2878 Q_UNUSED( context )
2879 Q_UNUSED( node )
2880
2881 if ( values.isEmpty() || values[0].isNull() )
2882 return QVariant();
2883
2884 return QgsStringUtils::unaccent( values[0].toString() );
2885}
2886
2887
2888static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2889{
2890 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2891 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2892 return string.right( pos );
2893}
2894
2895static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2896{
2897 if ( values.length() < 2 || values.length() > 3 )
2898 return QVariant();
2899
2900 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2901 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2902
2903 bool overlapping = false;
2904 if ( values.length() == 3 )
2905 {
2906 overlapping = values.at( 2 ).toBool();
2907 }
2908
2909 if ( substring.isEmpty() )
2910 return QVariant( 0 );
2911
2912 int count = 0;
2913 if ( overlapping )
2914 {
2915 count = input.count( substring );
2916 }
2917 else
2918 {
2919 int pos = 0;
2920 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2921 {
2922 count++;
2923 pos += substring.length();
2924 }
2925 }
2926
2927 return QVariant( count );
2928}
2929
2930static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2931{
2932 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2933 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2934 return string.left( pos );
2935}
2936
2937static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2938{
2939 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2940 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2941 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2942 return string.leftJustified( length, fill.at( 0 ), true );
2943}
2944
2945static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2946{
2947 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2948 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2949 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2950 return string.rightJustified( length, fill.at( 0 ), true );
2951}
2952
2953static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2954{
2955 if ( values.size() < 1 )
2956 {
2957 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2958 return QVariant();
2959 }
2960
2961 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2962 for ( int n = 1; n < values.length(); n++ )
2963 {
2964 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2965 }
2966 return string;
2967}
2968
2969
2970static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2971{
2972 return QVariant( QDateTime::currentDateTime() );
2973}
2974
2975static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2976{
2977 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2978 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2979 if ( format.isEmpty() && !language.isEmpty() )
2980 {
2981 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2982 return QVariant( QDate() );
2983 }
2984
2985 if ( format.isEmpty() && language.isEmpty() )
2986 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2987
2988 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2989 QLocale locale = QLocale();
2990 if ( !language.isEmpty() )
2991 {
2992 locale = QLocale( language );
2993 }
2994
2995 QDate date = locale.toDate( datestring, format );
2996 if ( !date.isValid() )
2997 {
2998 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2999 date = QDate();
3000 }
3001 return QVariant( date );
3002}
3003
3004static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3005{
3006 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3007 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3008 if ( format.isEmpty() && !language.isEmpty() )
3009 {
3010 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
3011 return QVariant( QTime() );
3012 }
3013
3014 if ( format.isEmpty() && language.isEmpty() )
3015 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
3016
3017 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3018 QLocale locale = QLocale();
3019 if ( !language.isEmpty() )
3020 {
3021 locale = QLocale( language );
3022 }
3023
3024 QTime time = locale.toTime( timestring, format );
3025 if ( !time.isValid() )
3026 {
3027 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
3028 time = QTime();
3029 }
3030 return QVariant( time );
3031}
3032
3033static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3034{
3035 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
3036}
3037
3038/*
3039 * DMS functions
3040 */
3041
3042static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3043{
3044 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3045 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3046 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3047
3048 QString formatString;
3049 if ( values.count() > 3 )
3050 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
3051
3053 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
3054 {
3056 }
3057 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
3058 {
3060 }
3061 else if ( !formatString.isEmpty() )
3062 {
3063 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
3064 return QVariant();
3065 }
3066
3067 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
3068 {
3069 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
3070 }
3071 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
3072 {
3073 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
3074 }
3075 else
3076 {
3077 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
3078 return QVariant();
3079 }
3080}
3081
3082static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3083{
3085 return floatToDegreeFormat( format, values, context, parent, node );
3086}
3087
3088static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3089{
3090 double value = 0.0;
3091 bool ok = false;
3092 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
3093
3094 return ok ? QVariant( value ) : QVariant();
3095}
3096
3097static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3098{
3100 return floatToDegreeFormat( format, values, context, parent, node );
3101}
3102
3103static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3104{
3105 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3106 return static_cast< int >( decimalDegrees );
3107}
3108
3109static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3110{
3111 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3112 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3113 return static_cast< int >( remainder * 60 );
3114}
3115
3116static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3117{
3118 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3119 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3120 const double remainderInMinutes = remainder * 60;
3121 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
3122 // do not truncate to int, this function returns decimal seconds!
3123 return remainderSecondsFraction * 60;
3124}
3125
3126static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3127{
3128 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3129 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3130 qint64 seconds = d2.secsTo( d1 );
3131 return QVariant::fromValue( QgsInterval( seconds ) );
3132}
3133
3134static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3135{
3136 if ( !values.at( 0 ).canConvert<QDate>() )
3137 return QVariant();
3138
3139 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
3140 if ( !date.isValid() )
3141 return QVariant();
3142
3143 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
3144 // (to match PostgreSQL behavior)
3145 return date.dayOfWeek() % 7;
3146}
3147
3148static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3149{
3150 QVariant value = values.at( 0 );
3151 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3152 if ( inter.isValid() )
3153 {
3154 return QVariant( inter.days() );
3155 }
3156 else
3157 {
3158 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3159 return QVariant( d1.date().day() );
3160 }
3161}
3162
3163static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3164{
3165 QVariant value = values.at( 0 );
3166 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3167 if ( inter.isValid() )
3168 {
3169 return QVariant( inter.years() );
3170 }
3171 else
3172 {
3173 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3174 return QVariant( d1.date().year() );
3175 }
3176}
3177
3178static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3179{
3180 QVariant value = values.at( 0 );
3181 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3182 if ( inter.isValid() )
3183 {
3184 return QVariant( inter.months() );
3185 }
3186 else
3187 {
3188 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3189 return QVariant( d1.date().month() );
3190 }
3191}
3192
3193static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3194{
3195 QVariant value = values.at( 0 );
3196 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3197 if ( inter.isValid() )
3198 {
3199 return QVariant( inter.weeks() );
3200 }
3201 else
3202 {
3203 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3204 return QVariant( d1.date().weekNumber() );
3205 }
3206}
3207
3208static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3209{
3210 QVariant value = values.at( 0 );
3211 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3212 if ( inter.isValid() )
3213 {
3214 return QVariant( inter.hours() );
3215 }
3216 else
3217 {
3218 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3219 return QVariant( t1.hour() );
3220 }
3221}
3222
3223static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3224{
3225 QVariant value = values.at( 0 );
3226 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3227 if ( inter.isValid() )
3228 {
3229 return QVariant( inter.minutes() );
3230 }
3231 else
3232 {
3233 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3234 return QVariant( t1.minute() );
3235 }
3236}
3237
3238static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3239{
3240 QVariant value = values.at( 0 );
3241 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3242 if ( inter.isValid() )
3243 {
3244 return QVariant( inter.seconds() );
3245 }
3246 else
3247 {
3248 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3249 return QVariant( t1.second() );
3250 }
3251}
3252
3253static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3254{
3255 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3256 if ( dt.isValid() )
3257 {
3258 return QVariant( dt.toMSecsSinceEpoch() );
3259 }
3260 else
3261 {
3262 return QVariant();
3263 }
3264}
3265
3266static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3267{
3268 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3269 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3270 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3271}
3272
3273static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3274{
3275 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3276 if ( parent->hasEvalError() )
3277 {
3278 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3279 return QVariant();
3280 }
3281 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3282 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3283}
3284
3285static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3287 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3288 if ( parent->hasEvalError() )
3289 {
3290 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3291 return QVariant();
3292 }
3293 bool ok;
3294 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3295}
3296
3297double qDateTimeToDecimalYear( const QDateTime &dateTime )
3298{
3299 if ( !dateTime.isValid() )
3300 {
3301 return 0.0;
3302 }
3303
3304 const int year = dateTime.date().year();
3305 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3306 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3307 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3308 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3309 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3310}
3311
3312static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3313{
3314 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3315 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3316 if ( parent->hasEvalError() )
3317 {
3318 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3319 return QVariant();
3320 }
3321 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3322 if ( parent->hasEvalError() )
3323 {
3324 return QVariant();
3325 }
3326 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3327 if ( parent->hasEvalError() )
3328 {
3329 return QVariant();
3330 }
3331 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3332 if ( parent->hasEvalError() )
3333 {
3334 return QVariant();
3335 }
3336 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3337
3338 const QgsMagneticModel model( name, filePath );
3339 try
3340 {
3341 double declination = 0;
3342 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3343 {
3344 return declination;
3345 }
3346 else
3347 {
3348 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3349 }
3350 }
3351 catch ( QgsNotSupportedException &e )
3352 {
3353 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3354 }
3355 return QVariant();
3356}
3357
3358static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3359{
3360 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3361 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3362 if ( parent->hasEvalError() )
3363 {
3364 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3365 return QVariant();
3366 }
3367 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3368 if ( parent->hasEvalError() )
3369 {
3370 return QVariant();
3371 }
3372 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3373 if ( parent->hasEvalError() )
3374 {
3375 return QVariant();
3376 }
3377 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3378 if ( parent->hasEvalError() )
3379 {
3380 return QVariant();
3381 }
3382 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3383
3384 const QgsMagneticModel model( name, filePath );
3385 try
3386 {
3387 double inclination = 0;
3388 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3389 {
3390 return inclination;
3391 }
3392 else
3393 {
3394 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3395 }
3396 }
3397 catch ( QgsNotSupportedException &e )
3398 {
3399 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3400 }
3401 return QVariant();
3402}
3403
3404static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3405{
3406 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3407 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3408 if ( parent->hasEvalError() )
3409 {
3410 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3411 return QVariant();
3412 }
3413 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3414 if ( parent->hasEvalError() )
3415 {
3416 return QVariant();
3417 }
3418 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3419 if ( parent->hasEvalError() )
3420 {
3421 return QVariant();
3422 }
3423 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3424 if ( parent->hasEvalError() )
3425 {
3426 return QVariant();
3427 }
3428 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3429
3430 const QgsMagneticModel model( name, filePath );
3431 try
3432 {
3433 double declination = 0;
3434 double Bx = 0;
3435 double By = 0;
3436 double Bz = 0;
3437 double Bxt = 0;
3438 double Byt = 0;
3439 double Bzt = 0;
3440
3441 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3442 {
3443 double H = 0;
3444 double F = 0;
3445 double D = 0;
3446 double I = 0;
3447 double Ht = 0;
3448 double Ft = 0;
3449 double Dt = 0;
3450 double It = 0;
3451 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3452 {
3453 return Dt;
3454 }
3455 else
3456 {
3457 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3458 }
3459 return declination;
3460 }
3461 else
3462 {
3463 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3464 }
3465 }
3466 catch ( QgsNotSupportedException &e )
3467 {
3468 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3469 }
3470 return QVariant();
3471}
3472
3473static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3474{
3475 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3476 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3477 if ( parent->hasEvalError() )
3478 {
3479 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3480 return QVariant();
3481 }
3482 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3483 if ( parent->hasEvalError() )
3484 {
3485 return QVariant();
3486 }
3487 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3488 if ( parent->hasEvalError() )
3489 {
3490 return QVariant();
3491 }
3492 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3493 if ( parent->hasEvalError() )
3494 {
3495 return QVariant();
3496 }
3497 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3498
3499 const QgsMagneticModel model( name, filePath );
3500 try
3501 {
3502 double declination = 0;
3503 double Bx = 0;
3504 double By = 0;
3505 double Bz = 0;
3506 double Bxt = 0;
3507 double Byt = 0;
3508 double Bzt = 0;
3509
3510 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3511 {
3512 double H = 0;
3513 double F = 0;
3514 double D = 0;
3515 double I = 0;
3516 double Ht = 0;
3517 double Ft = 0;
3518 double Dt = 0;
3519 double It = 0;
3520 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3521 {
3522 return It;
3523 }
3524 else
3525 {
3526 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3527 }
3528 return declination;
3529 }
3530 else
3532 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3533 }
3534 }
3535 catch ( QgsNotSupportedException &e )
3536 {
3537 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3538 }
3539 return QVariant();
3540}
3541
3542#define ENSURE_GEOM_TYPE( f, g, geomtype ) \
3543 if ( !( f ).hasGeometry() ) \
3544 return QVariant(); \
3545 QgsGeometry g = ( f ).geometry(); \
3546 if ( ( g ).type() != ( geomtype ) ) \
3547 return QVariant();
3548
3549static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3550{
3551 FEAT_FROM_CONTEXT( context, f )
3553 if ( g.isMultipart() )
3554 {
3555 return g.asMultiPoint().at( 0 ).x();
3556 }
3557 else
3558 {
3559 return g.asPoint().x();
3560 }
3561}
3562
3563static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3564{
3565 FEAT_FROM_CONTEXT( context, f )
3567 if ( g.isMultipart() )
3568 {
3569 return g.asMultiPoint().at( 0 ).y();
3570 }
3571 else
3572 {
3573 return g.asPoint().y();
3574 }
3575}
3576
3577static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3578{
3579 FEAT_FROM_CONTEXT( context, f )
3581
3582 if ( g.isEmpty() )
3583 return QVariant();
3584
3585 const QgsAbstractGeometry *abGeom = g.constGet();
3586
3587 if ( g.isEmpty() || !abGeom->is3D() )
3588 return QVariant();
3589
3590 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3591 {
3592 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3593 if ( point )
3594 return point->z();
3595 }
3596 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3597 {
3598 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3599 {
3600 if ( collection->numGeometries() > 0 )
3601 {
3602 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3603 return point->z();
3604 }
3605 }
3606 }
3607
3608 return QVariant();
3609}
3610
3611static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3612{
3613 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3614 if ( geom.isNull() )
3615 return QVariant();
3616
3617 bool isValid = geom.isGeosValid();
3618
3619 return QVariant( isValid );
3620}
3621
3622static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3623{
3624 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3625 if ( geom.isNull() )
3626 return QVariant();
3627
3628 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3629#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
3631#else
3633#endif
3634 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3636 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3638
3639 const bool keepCollapsed = values.value( 2 ).toBool();
3640
3641 QgsGeometry valid;
3642 try
3643 {
3644 valid = geom.makeValid( method, keepCollapsed );
3645 }
3646 catch ( QgsNotSupportedException & )
3647 {
3648 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3649 return QVariant();
3650 }
3651
3652 return QVariant::fromValue( valid );
3653}
3654
3655static QVariant fcnGeometryCollectionAsArray( 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 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3662 QVariantList array;
3663 for ( int i = 0; i < multiGeom.size(); ++i )
3664 {
3665 array += QVariant::fromValue( multiGeom.at( i ) );
3666 }
3667
3668 return array;
3669}
3670
3671static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3672{
3673 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3674 if ( geom.isNull() )
3675 return QVariant();
3676
3677 //if single point, return the point's x coordinate
3678 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3679 {
3680 return geom.asPoint().x();
3681 }
3682
3683 //otherwise return centroid x
3684 QgsGeometry centroid = geom.centroid();
3685 QVariant result( centroid.asPoint().x() );
3686 return result;
3687}
3688
3689static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3690{
3691 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3692 if ( geom.isNull() )
3693 return QVariant();
3694
3695 //if single point, return the point's y coordinate
3696 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3697 {
3698 return geom.asPoint().y();
3699 }
3700
3701 //otherwise return centroid y
3702 QgsGeometry centroid = geom.centroid();
3703 QVariant result( centroid.asPoint().y() );
3704 return result;
3705}
3706
3707static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3708{
3709 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3710 if ( geom.isNull() )
3711 return QVariant(); //or 0?
3712
3713 if ( !geom.constGet()->is3D() )
3714 return QVariant();
3715
3716 //if single point, return the point's z coordinate
3717 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3718 {
3720 if ( point )
3721 return point->z();
3722 }
3723 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3724 {
3726 {
3727 if ( collection->numGeometries() == 1 )
3728 {
3729 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3730 return point->z();
3731 }
3732 }
3733 }
3734
3735 return QVariant();
3736}
3737
3738static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3739{
3740 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3741 if ( geom.isNull() )
3742 return QVariant(); //or 0?
3743
3744 if ( !geom.constGet()->isMeasure() )
3745 return QVariant();
3746
3747 //if single point, return the point's m value
3748 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3749 {
3751 if ( point )
3752 return point->m();
3753 }
3754 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3755 {
3757 {
3758 if ( collection->numGeometries() == 1 )
3759 {
3760 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3761 return point->m();
3762 }
3763 }
3764 }
3765
3766 return QVariant();
3767}
3768
3769static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3770{
3771 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3772
3773 if ( geom.isNull() )
3774 return QVariant();
3775
3776 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3777
3778 if ( idx < 0 )
3779 {
3780 //negative idx
3781 int count = geom.constGet()->nCoordinates();
3782 idx = count + idx;
3783 }
3784 else
3785 {
3786 //positive idx is 1 based
3787 idx -= 1;
3788 }
3789
3790 QgsVertexId vId;
3791 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3792 {
3793 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3794 return QVariant();
3795 }
3796
3797 QgsPoint point = geom.constGet()->vertexAt( vId );
3798 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3799}
3800
3801static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3802{
3803 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3804
3805 if ( geom.isNull() )
3806 return QVariant();
3807
3808 QgsVertexId vId;
3809 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3810 {
3811 return QVariant();
3812 }
3813
3814 QgsPoint point = geom.constGet()->vertexAt( vId );
3815 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3816}
3817
3818static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3819{
3820 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3821
3822 if ( geom.isNull() )
3823 return QVariant();
3824
3825 QgsVertexId vId;
3826 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3827 {
3828 return QVariant();
3829 }
3830
3831 QgsPoint point = geom.constGet()->vertexAt( vId );
3832 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3833}
3834
3835static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3836{
3837 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3838
3839 if ( geom.isNull() )
3840 return QVariant();
3841
3842 bool ignoreClosing = false;
3843 if ( values.length() > 1 )
3844 {
3845 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3846 }
3847
3848 QgsMultiPoint *mp = new QgsMultiPoint();
3849
3850 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3851 for ( const QgsRingSequence &part : sequence )
3852 {
3853 for ( const QgsPointSequence &ring : part )
3854 {
3855 bool skipLast = false;
3856 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3857 {
3858 skipLast = true;
3859 }
3860
3861 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++i )
3862 {
3863 mp->addGeometry( ring.at( i ).clone() );
3864 }
3865 }
3866 }
3867
3868 return QVariant::fromValue( QgsGeometry( mp ) );
3869}
3870
3871static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3872{
3873 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3874
3875 if ( geom.isNull() )
3876 return QVariant();
3877
3878 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3879
3880 //OK, now we have a complete list of segmentized lines from the geometry
3882 for ( QgsLineString *line : linesToProcess )
3883 {
3884 for ( int i = 0; i < line->numPoints() - 1; ++i )
3885 {
3887 segment->setPoints( QgsPointSequence() << line->pointN( i ) << line->pointN( i + 1 ) );
3888 ml->addGeometry( segment );
3889 }
3890 delete line;
3891 }
3892
3893 return QVariant::fromValue( QgsGeometry( ml ) );
3894}
3895
3896static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3897{
3898 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3899
3900 if ( geom.isNull() )
3901 return QVariant();
3902
3904 if ( !curvePolygon && geom.isMultipart() )
3905 {
3907 {
3908 if ( collection->numGeometries() == 1 )
3909 {
3910 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3911 }
3912 }
3913 }
3914
3915 if ( !curvePolygon )
3916 return QVariant();
3917
3918 //idx is 1 based
3919 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3920
3921 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3922 return QVariant();
3923
3924 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3925 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3926 return result;
3927}
3928
3929static QVariant fcnGeometryN( 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 ( !collection )
3938 return QVariant();
3939
3940 //idx is 1 based
3941 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3942
3943 if ( idx < 0 || idx >= collection->numGeometries() )
3944 return QVariant();
3945
3946 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3947 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3948 return result;
3949}
3950
3951static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3952{
3953 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3954
3955 if ( geom.isNull() )
3956 return QVariant();
3957
3958 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3959 if ( !boundary )
3960 return QVariant();
3961
3962 return QVariant::fromValue( QgsGeometry( boundary ) );
3963}
3964
3965static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3966{
3967 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3968
3969 if ( geom.isNull() )
3970 return QVariant();
3971
3972 QgsGeometry merged = geom.mergeLines();
3973 if ( merged.isNull() )
3974 return QVariant();
3975
3976 return QVariant::fromValue( merged );
3977}
3978
3979static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3980{
3981 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3982 if ( geom.isNull() )
3983 return QVariant();
3984
3985 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3986 if ( geom2.isNull() )
3987 return QVariant();
3988
3989 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3990 if ( sharedPaths.isNull() )
3991 return QVariant();
3992
3993 return QVariant::fromValue( sharedPaths );
3994}
3995
3996
3997static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3998{
3999 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4000
4001 if ( geom.isNull() )
4002 return QVariant();
4003
4004 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4005
4006 QgsGeometry simplified = geom.simplify( tolerance );
4007 if ( simplified.isNull() )
4008 return QVariant();
4009
4010 return simplified;
4011}
4012
4013static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4014{
4015 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4016
4017 if ( geom.isNull() )
4018 return QVariant();
4019
4020 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4021
4023
4024 QgsGeometry simplified = simplifier.simplify( geom );
4025 if ( simplified.isNull() )
4026 return QVariant();
4027
4028 return simplified;
4029}
4030
4031static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4032{
4033 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4034
4035 if ( geom.isNull() )
4036 return QVariant();
4037
4038 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
4039 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
4040 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4041 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
4042
4043 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
4044 if ( smoothed.isNull() )
4045 return QVariant();
4046
4047 return smoothed;
4048}
4049
4050static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4051{
4052 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4053
4054 if ( geom.isNull() )
4055 return QVariant();
4056
4057 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4058 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4059 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4060
4061 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
4062 if ( waved.isNull() )
4063 return QVariant();
4064
4065 return waved;
4066}
4067
4068static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4069{
4070 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4071
4072 if ( geom.isNull() )
4073 return QVariant();
4074
4075 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4076 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4077 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4078 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4079 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4080
4081 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4082 if ( waved.isNull() )
4083 return QVariant();
4084
4085 return waved;
4086}
4087
4088static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4089{
4090 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4091
4092 if ( geom.isNull() )
4093 return QVariant();
4094
4095 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4096 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4097 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4098
4099 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
4100 if ( waved.isNull() )
4101 return QVariant();
4102
4103 return waved;
4104}
4105
4106static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4107{
4108 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4109
4110 if ( geom.isNull() )
4111 return QVariant();
4112
4113 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4114 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4115 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4116 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4117 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4118
4119 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4120 if ( waved.isNull() )
4121 return QVariant();
4122
4123 return waved;
4124}
4125
4126static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4127{
4128 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4129
4130 if ( geom.isNull() )
4131 return QVariant();
4132
4133 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4134 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4135 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4136
4137 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
4138 if ( waved.isNull() )
4139 return QVariant();
4140
4141 return waved;
4142}
4143
4144static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4145{
4146 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4147
4148 if ( geom.isNull() )
4149 return QVariant();
4150
4151 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4152 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4153 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4154 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4155 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4156
4157 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4158 if ( waved.isNull() )
4159 return QVariant();
4160
4161 return waved;
4162}
4163
4164static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4165{
4166 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4167
4168 if ( geom.isNull() )
4169 return QVariant();
4170
4171 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4172 QVector< double > dashPattern;
4173 dashPattern.reserve( pattern.size() );
4174 for ( const QVariant &value : std::as_const( pattern ) )
4175 {
4176 bool ok = false;
4177 double v = value.toDouble( &ok );
4178 if ( ok )
4179 {
4180 dashPattern << v;
4181 }
4182 else
4183 {
4184 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4185 return QgsGeometry();
4186 }
4187 }
4188
4189 if ( dashPattern.size() % 2 != 0 )
4190 {
4191 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4192 return QgsGeometry();
4193 }
4194
4195 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4197 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4199 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4201 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4203 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4205 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4207 else
4208 {
4209 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4210 return QgsGeometry();
4211 }
4212
4213 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4215 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4217 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4219 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4221 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4223 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4225 else
4226 {
4227 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4228 return QgsGeometry();
4229 }
4230
4231 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4233 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4235 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4237 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4239 else
4240 {
4241 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4242 return QgsGeometry();
4243 }
4244
4245 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4246
4247 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4248 if ( result.isNull() )
4249 return QVariant();
4250
4251 return result;
4252}
4253
4254static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4255{
4256 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4257
4258 if ( geom.isNull() )
4259 return QVariant();
4260
4261 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4262 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4263 if ( densified.isNull() )
4264 return QVariant();
4265
4266 return densified;
4267}
4268
4269static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4270{
4271 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4272
4273 if ( geom.isNull() )
4274 return QVariant();
4275
4276 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4277 const QgsGeometry densified = geom.densifyByDistance( distance );
4278 if ( densified.isNull() )
4279 return QVariant();
4280
4281 return densified;
4282}
4283
4284static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4285{
4286 QVariantList list;
4287 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4288 {
4289 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4290 }
4291 else
4292 {
4293 list = values;
4294 }
4295
4296 QVector< QgsGeometry > parts;
4297 parts.reserve( list.size() );
4298 for ( const QVariant &value : std::as_const( list ) )
4299 {
4300 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4301 if ( part.isNull() )
4302 return QgsGeometry();
4303 parts << part;
4304 }
4305
4306 return QgsGeometry::collectGeometry( parts );
4307}
4308
4309static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4310{
4311 if ( values.count() < 2 || values.count() > 4 )
4312 {
4313 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4314 return QVariant();
4315 }
4316
4317 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4318 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4319 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4320 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4321 switch ( values.count() )
4322 {
4323 case 2:
4324 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4325 case 3:
4326 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4327 case 4:
4328 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4329 }
4330 return QVariant(); //avoid warning
4331}
4332
4333static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4334{
4335 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4336 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4337 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4338 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4339}
4340
4341static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4342{
4343 if ( values.empty() )
4344 {
4345 return QVariant();
4346 }
4347
4348 QVector<QgsPoint> points;
4349 points.reserve( values.count() );
4350
4351 auto addPoint = [&points]( const QgsGeometry &geom ) {
4352 if ( geom.isNull() )
4353 return;
4354
4355 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4356 return;
4357
4359 if ( !point )
4360 return;
4361
4362 points << *point;
4363 };
4364
4365 for ( const QVariant &value : values )
4366 {
4367 if ( value.userType() == QMetaType::Type::QVariantList )
4368 {
4369 const QVariantList list = value.toList();
4370 for ( const QVariant &v : list )
4371 {
4372 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4373 }
4374 }
4375 else
4376 {
4377 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4378 }
4379 }
4380
4381 if ( points.count() < 2 )
4382 return QVariant();
4383
4384 return QgsGeometry( new QgsLineString( points ) );
4385}
4386
4387static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4388{
4389 if ( values.count() < 1 )
4390 {
4391 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4392 return QVariant();
4393 }
4394
4395 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4396
4397 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4398 return outerRing; // if it's already a polygon we have nothing to do
4399
4400 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4401 return QVariant();
4402
4403 auto polygon = std::make_unique< QgsPolygon >();
4404
4405 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4406 if ( !exteriorRing && outerRing.isMultipart() )
4407 {
4409 {
4410 if ( collection->numGeometries() == 1 )
4411 {
4412 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4413 }
4414 }
4415 }
4416
4417 if ( !exteriorRing )
4418 return QVariant();
4419
4420 polygon->setExteriorRing( exteriorRing->segmentize() );
4421
4422
4423 for ( int i = 1; i < values.count(); ++i )
4424 {
4425 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4426 if ( ringGeom.isNull() )
4427 continue;
4428
4429 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4430 continue;
4431
4432 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4433 if ( !ring && ringGeom.isMultipart() )
4434 {
4436 {
4437 if ( collection->numGeometries() == 1 )
4438 {
4439 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4440 }
4441 }
4442 }
4443
4444 if ( !ring )
4445 continue;
4446
4447 polygon->addInteriorRing( ring->segmentize() );
4448 }
4449
4450 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4451}
4452
4453static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4454{
4455 auto tr = std::make_unique<QgsTriangle>();
4456 auto lineString = std::make_unique<QgsLineString>();
4457 lineString->clear();
4458
4459 for ( const QVariant &value : values )
4460 {
4461 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4462 if ( geom.isNull() )
4463 return QVariant();
4464
4465 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4466 return QVariant();
4467
4469 if ( !point && geom.isMultipart() )
4470 {
4472 {
4473 if ( collection->numGeometries() == 1 )
4474 {
4475 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4476 }
4477 }
4478 }
4479
4480 if ( !point )
4481 return QVariant();
4482
4483 lineString->addVertex( *point );
4484 }
4485
4486 tr->setExteriorRing( lineString.release() );
4487
4488 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4489}
4490
4491static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4492{
4493 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4494 if ( geom.isNull() )
4495 return QVariant();
4496
4497 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4498 return QVariant();
4499
4500 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4501 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4502
4503 if ( segment < 3 )
4504 {
4505 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4506 return QVariant();
4507 }
4509 if ( !point && geom.isMultipart() )
4510 {
4512 {
4513 if ( collection->numGeometries() == 1 )
4514 {
4515 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4516 }
4517 }
4518 }
4519 if ( !point )
4520 return QVariant();
4521
4522 QgsCircle circ( *point, radius );
4523 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4524}
4525
4526static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4527{
4528 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4529 if ( geom.isNull() )
4530 return QVariant();
4531
4532 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4533 return QVariant();
4534
4535 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4536 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4537 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4538 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4539 if ( segment < 3 )
4540 {
4541 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4542 return QVariant();
4543 }
4545 if ( !point && geom.isMultipart() )
4546 {
4548 {
4549 if ( collection->numGeometries() == 1 )
4550 {
4551 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4552 }
4553 }
4554 }
4555 if ( !point )
4556 return QVariant();
4557
4558 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4559 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4560}
4561
4562static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4563{
4564 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4565 if ( pt1.isNull() )
4566 return QVariant();
4567
4568 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4569 return QVariant();
4570
4571 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4572 if ( pt2.isNull() )
4573 return QVariant();
4574
4575 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4576 return QVariant();
4577
4578 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4579 if ( nbEdges < 3 )
4580 {
4581 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4582 return QVariant();
4583 }
4584
4585 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4587 {
4588 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4589 return QVariant();
4590 }
4591
4593 if ( !center && pt1.isMultipart() )
4594 {
4596 {
4597 if ( collection->numGeometries() == 1 )
4598 {
4599 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4600 }
4601 }
4602 }
4603 if ( !center )
4604 return QVariant();
4605
4607 if ( !corner && pt2.isMultipart() )
4608 {
4610 {
4611 if ( collection->numGeometries() == 1 )
4612 {
4613 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4614 }
4615 }
4616 }
4617 if ( !corner )
4618 return QVariant();
4619
4620 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4621
4622 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4623}
4624
4625static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4626{
4627 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4628 if ( pt1.isNull() )
4629 return QVariant();
4630 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4631 return QVariant();
4632
4633 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4634 if ( pt2.isNull() )
4635 return QVariant();
4636 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4637 return QVariant();
4638
4639 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4640 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4641 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4642
4643 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4644}
4645
4646static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4647{
4648 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4649 if ( pt1.isNull() )
4650 return QVariant();
4651 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4652 return QVariant();
4653
4654 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4655 if ( pt2.isNull() )
4656 return QVariant();
4657 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4658 return QVariant();
4659
4660 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4661 if ( pt3.isNull() )
4662 return QVariant();
4663 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4664 return QVariant();
4665
4666 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4667 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4668 {
4669 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
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 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4675 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4676 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4677}
4678
4679static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4680{
4681 if ( geom.isNull() )
4682 return QVariant();
4683
4684 if ( idx < 0 )
4685 {
4686 idx += geom.constGet()->nCoordinates();
4687 }
4688 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4689 {
4690 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4691 return QVariant();
4692 }
4693 return QVariant::fromValue( geom.vertexAt( idx ) );
4694}
4695
4696// function used for the old $ style
4697static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4698{
4699 FEAT_FROM_CONTEXT( context, feature )
4700 const QgsGeometry geom = feature.geometry();
4701 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4702
4703 const QVariant v = pointAt( geom, idx, parent );
4704
4705 if ( !v.isNull() )
4706 return QVariant( v.value<QgsPoint>().x() );
4707 else
4708 return QVariant();
4709}
4710static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4711{
4712 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))
4713 {
4714 return fcnOldXat( values, f, parent, node );
4715 }
4716 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)
4717 {
4718 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4719 }
4720
4721 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4722 if ( geom.isNull() )
4723 {
4724 return QVariant();
4725 }
4726
4727 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4728
4729 const QVariant v = pointAt( geom, vertexNumber, parent );
4730 if ( !v.isNull() )
4731 return QVariant( v.value<QgsPoint>().x() );
4732 else
4733 return QVariant();
4734}
4735
4736// function used for the old $ style
4737static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4738{
4739 FEAT_FROM_CONTEXT( context, feature )
4740 const QgsGeometry geom = feature.geometry();
4741 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4742
4743 const QVariant v = pointAt( geom, idx, parent );
4744
4745 if ( !v.isNull() )
4746 return QVariant( v.value<QgsPoint>().y() );
4747 else
4748 return QVariant();
4749}
4750static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4751{
4752 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))
4753 {
4754 return fcnOldYat( values, f, parent, node );
4755 }
4756 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)
4757 {
4758 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4759 }
4760
4761 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4762 if ( geom.isNull() )
4763 {
4764 return QVariant();
4765 }
4766
4767 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4768
4769 const QVariant v = pointAt( geom, vertexNumber, parent );
4770 if ( !v.isNull() )
4771 return QVariant( v.value<QgsPoint>().y() );
4772 else
4773 return QVariant();
4774}
4775
4776static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4777{
4778 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4779 if ( geom.isNull() )
4780 {
4781 return QVariant();
4782 }
4783
4784 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4785
4786 const QVariant v = pointAt( geom, vertexNumber, parent );
4787 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4788 return QVariant( v.value<QgsPoint>().z() );
4789 else
4790 return QVariant();
4791}
4792
4793static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4794{
4795 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4796 if ( geom.isNull() )
4797 {
4798 return QVariant();
4799 }
4800
4801 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4802
4803 const QVariant v = pointAt( geom, vertexNumber, parent );
4804 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4805 return QVariant( v.value<QgsPoint>().m() );
4806 else
4807 return QVariant();
4808}
4809
4810
4811static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4812{
4813 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4814 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4815 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4816 return result;
4817}
4818
4819static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4820{
4821 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4822 if ( wkb.isNull() )
4823 return QVariant();
4824
4825 QgsGeometry geom;
4826 geom.fromWkb( wkb );
4827 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4828}
4829
4830static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4831{
4832 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4833 QgsOgcUtils::Context ogcContext;
4834 if ( context )
4835 {
4836 QgsWeakMapLayerPointer mapLayerPtr { context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4837 if ( mapLayerPtr )
4838 {
4839 ogcContext.layer = mapLayerPtr.data();
4840 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4841 }
4842 }
4843 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4844 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4845 return result;
4846}
4847
4848static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4849{
4850 FEAT_FROM_CONTEXT( context, f )
4852 QgsDistanceArea *calc = parent->geomCalculator();
4853 if ( calc )
4854 {
4855 try
4856 {
4857 double area = calc->measureArea( f.geometry() );
4858 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4859 return QVariant( area );
4860 }
4861 catch ( QgsCsException & )
4862 {
4863 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4864 return QVariant();
4865 }
4866 }
4867 else
4868 {
4869 return QVariant( f.geometry().area() );
4870 }
4871}
4872
4873static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4874{
4875 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4876
4877 if ( geom.type() != Qgis::GeometryType::Polygon )
4878 return QVariant();
4879
4880 return QVariant( geom.area() );
4881}
4882
4883static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4884{
4885 FEAT_FROM_CONTEXT( context, f )
4887 QgsDistanceArea *calc = parent->geomCalculator();
4888 if ( calc )
4889 {
4890 try
4891 {
4892 double len = calc->measureLength( f.geometry() );
4893 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4894 return QVariant( len );
4895 }
4896 catch ( QgsCsException & )
4897 {
4898 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4899 return QVariant();
4900 }
4901 }
4902 else
4903 {
4904 return QVariant( f.geometry().length() );
4905 }
4906}
4907
4908static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4909{
4910 FEAT_FROM_CONTEXT( context, f )
4912 QgsDistanceArea *calc = parent->geomCalculator();
4913 if ( calc )
4914 {
4915 try
4916 {
4917 double len = calc->measurePerimeter( f.geometry() );
4918 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4919 return QVariant( len );
4920 }
4921 catch ( QgsCsException & )
4922 {
4923 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4924 return QVariant();
4925 }
4926 }
4927 else
4928 {
4929 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4930 }
4931}
4932
4933static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4934{
4935 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4936
4937 if ( geom.type() != Qgis::GeometryType::Polygon )
4938 return QVariant();
4939
4940 //length for polygons = perimeter
4941 return QVariant( geom.length() );
4942}
4943
4944static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4945{
4946 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4947 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4948}
4949
4950static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4951{
4952 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4953 if ( geom.isNull() )
4954 return QVariant();
4955
4956 return QVariant( geom.constGet()->partCount() );
4957}
4958
4959static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4960{
4961 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4962 if ( geom.isNull() )
4963 return QVariant();
4964
4965 return QVariant( geom.isMultipart() );
4966}
4967
4968static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4969{
4970 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4971
4972 if ( geom.isNull() )
4973 return QVariant();
4974
4976 if ( curvePolygon )
4977 return QVariant( curvePolygon->numInteriorRings() );
4978
4980 if ( collection )
4981 {
4982 //find first CurvePolygon in collection
4983 for ( int i = 0; i < collection->numGeometries(); ++i )
4984 {
4985 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4986 if ( !curvePolygon )
4987 continue;
4988
4989 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4990 }
4991 }
4992
4993 return QVariant();
4994}
4995
4996static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4997{
4998 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4999
5000 if ( geom.isNull() )
5001 return QVariant();
5002
5004 if ( curvePolygon )
5005 return QVariant( curvePolygon->ringCount() );
5006
5007 bool foundPoly = false;
5008 int ringCount = 0;
5010 if ( collection )
5011 {
5012 //find CurvePolygons in collection
5013 for ( int i = 0; i < collection->numGeometries(); ++i )
5014 {
5015 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5016 if ( !curvePolygon )
5017 continue;
5018
5019 foundPoly = true;
5020 ringCount += curvePolygon->ringCount();
5021 }
5022 }
5023
5024 if ( !foundPoly )
5025 return QVariant();
5026
5027 return QVariant( ringCount );
5028}
5029
5030static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5031{
5032 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5033 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
5034 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
5035 return result;
5036}
5037
5038static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5039{
5040 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5041 return QVariant::fromValue( geom.boundingBox().width() );
5042}
5043
5044static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5045{
5046 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5047 return QVariant::fromValue( geom.boundingBox().height() );
5048}
5049
5050static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5051{
5052 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5053 if ( geom.isNull() )
5054 return QVariant();
5055
5057}
5058
5059static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5060{
5061 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5062 return QVariant::fromValue( geom.boundingBox().xMinimum() );
5063}
5064
5065static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5066{
5067 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5068 return QVariant::fromValue( geom.boundingBox().xMaximum() );
5069}
5070
5071static QVariant fcnYMin( 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().yMinimum() );
5075}
5076
5077static QVariant fcnYMax( 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().yMaximum() );
5081}
5082
5083static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5084{
5085 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5086
5087 if ( geom.isNull() || geom.isEmpty() )
5088 return QVariant();
5089
5090 if ( !geom.constGet()->is3D() )
5091 return QVariant();
5092
5093 double max = std::numeric_limits< double >::lowest();
5094
5095 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5096 {
5097 double z = ( *it ).z();
5098
5099 if ( max < z )
5100 max = z;
5101 }
5102
5103 if ( max == std::numeric_limits< double >::lowest() )
5104 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5105
5106 return QVariant( max );
5107}
5108
5109static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5110{
5111 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5112
5113 if ( geom.isNull() || geom.isEmpty() )
5114 return QVariant();
5115
5116 if ( !geom.constGet()->is3D() )
5117 return QVariant();
5118
5119 double min = std::numeric_limits< double >::max();
5120
5121 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5122 {
5123 double z = ( *it ).z();
5124
5125 if ( z < min )
5126 min = z;
5127 }
5128
5129 if ( min == std::numeric_limits< double >::max() )
5130 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5131
5132 return QVariant( min );
5133}
5134
5135static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5136{
5137 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5138
5139 if ( geom.isNull() || geom.isEmpty() )
5140 return QVariant();
5141
5142 if ( !geom.constGet()->isMeasure() )
5143 return QVariant();
5144
5145 double min = std::numeric_limits< double >::max();
5146
5147 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5148 {
5149 double m = ( *it ).m();
5150
5151 if ( m < min )
5152 min = m;
5153 }
5154
5155 if ( min == std::numeric_limits< double >::max() )
5156 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5157
5158 return QVariant( min );
5159}
5160
5161static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5162{
5163 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5164
5165 if ( geom.isNull() || geom.isEmpty() )
5166 return QVariant();
5167
5168 if ( !geom.constGet()->isMeasure() )
5169 return QVariant();
5170
5171 double max = std::numeric_limits< double >::lowest();
5172
5173 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5174 {
5175 double m = ( *it ).m();
5176
5177 if ( max < m )
5178 max = m;
5179 }
5180
5181 if ( max == std::numeric_limits< double >::lowest() )
5182 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5183
5184 return QVariant( max );
5185}
5186
5187static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5188{
5189 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5191 if ( !curve )
5192 {
5193 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5194 return QVariant();
5195 }
5196
5197 return QVariant( curve->sinuosity() );
5198}
5199
5200static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5201{
5202 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5203 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5204 if ( !curve )
5205 {
5206 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5207 return QVariant();
5208 }
5209
5210 return QVariant( curve->straightDistance2d() );
5211}
5212
5213static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5214{
5215 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5217
5218 if ( !poly )
5219 {
5220 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5221 return QVariant();
5222 }
5223
5224 return QVariant( poly->roundness() );
5225}
5226
5227
5228static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5229{
5230 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5231 if ( geom.isNull() )
5232 return QVariant();
5233
5234 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5235 flipped->swapXy();
5236 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5237}
5238
5239static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5240{
5241 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5242 if ( fGeom.isNull() )
5243 return QVariant();
5244
5245 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5246 if ( !curve && fGeom.isMultipart() )
5247 {
5249 {
5250 if ( collection->numGeometries() == 1 )
5251 {
5252 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5253 }
5254 }
5255 }
5256
5257 if ( !curve )
5258 return QVariant();
5259
5260 return QVariant::fromValue( curve->isClosed() );
5261}
5262
5263static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5264{
5265 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5266
5267 if ( geom.isNull() )
5268 return QVariant();
5269
5270 QVariant result;
5271 if ( !geom.isMultipart() )
5272 {
5274
5275 if ( !line )
5276 return QVariant();
5277
5278 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5279 closedLine->close();
5280
5281 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5282 }
5283 else
5284 {
5286 if ( !collection )
5287 return QVariant();
5288
5289 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5290
5291 for ( int i = 0; i < collection->numGeometries(); ++i )
5292 {
5293 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5294 {
5295 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5296 closedLine->close();
5297
5298 closed->addGeometry( closedLine.release() );
5299 }
5300 }
5301 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5302 }
5303
5304 return result;
5305}
5306
5307static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5308{
5309 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5310 if ( fGeom.isNull() )
5311 return QVariant();
5312
5313 return QVariant::fromValue( fGeom.isEmpty() );
5314}
5315
5316static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5317{
5318 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5319 return QVariant::fromValue( true );
5320
5321 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5322 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5323}
5324
5325static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5326{
5327 if ( values.length() < 2 || values.length() > 3 )
5328 return QVariant();
5329
5330 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5331 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5332
5333 if ( fGeom.isNull() || sGeom.isNull() )
5334 return QVariant();
5335
5336 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5337
5338 if ( values.length() == 2 )
5339 {
5340 //two geometry arguments, return relation
5341 QString result = engine->relate( sGeom.constGet() );
5342 return QVariant::fromValue( result );
5343 }
5344 else
5345 {
5346 //three arguments, test pattern
5347 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5348 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5349 return QVariant::fromValue( result );
5350 }
5351}
5352
5353static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5354{
5355 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5356 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5357 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5358}
5359static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5360{
5361 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5362 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5363 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5364}
5365static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5366{
5367 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5368 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5369 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5370}
5371static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5372{
5373 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5374 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5375 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5376}
5377static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5378{
5379 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5380 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5381 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5382}
5383static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5384{
5385 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5386 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5387 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5388}
5389static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5390{
5391 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5392 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5393 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5394}
5395static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5396{
5397 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5398 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5399 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5400}
5401
5402static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5403{
5404 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5405 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5406 return fGeom.isExactlyEqual( sGeom ) ? TVL_True : TVL_False;
5407}
5408
5409static QVariant fcnIsEqualsExact( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5410{
5411 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5412 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5413 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5414
5415 bool ok;
5416 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5417 if ( !ok )
5418 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5419
5420 QVariant ret = TVL_False;
5421 try
5422 {
5423 ret = fGeom.isExactlyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5424 }
5425 catch ( QgsNotSupportedException &e )
5426 {
5427 SET_EVAL_ERROR( e.what() );
5428 }
5429 return ret;
5430}
5431
5432static QVariant fcnIsEqualsTopological( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5433{
5434 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5435 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5436 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5437
5438 bool ok;
5439 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
5440 if ( !ok )
5441 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5442
5443 QVariant ret = TVL_False;
5444 try
5445 {
5446 ret = fGeom.isTopologicallyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5447 }
5448 catch ( QgsNotSupportedException &e )
5449 {
5450 SET_EVAL_ERROR( e.what() );
5451 }
5452 return ret;
5453}
5454
5455static QVariant fcnIsEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5456{
5457 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5458 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5459 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5460
5461 bool ok;
5462 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5463 if ( !ok )
5464 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5465
5466 double epsilon = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5467
5468 QVariant ret = TVL_False;
5469 try
5470 {
5471 ret = fGeom.isFuzzyEqual( sGeom, epsilon, backend ) ? TVL_True : TVL_False;
5472 }
5473 catch ( QgsNotSupportedException &e )
5474 {
5475 SET_EVAL_ERROR( e.what() );
5476 }
5477 return ret;
5478}
5479
5480static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5481{
5482 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5483 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5484 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5485 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5486 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5487 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5488
5490 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5491 capStyle = Qgis::EndCapStyle::Flat;
5492 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5493 capStyle = Qgis::EndCapStyle::Square;
5494
5496 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5497 joinStyle = Qgis::JoinStyle::Miter;
5498 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5499 joinStyle = Qgis::JoinStyle::Bevel;
5500
5501 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5502 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5503 return result;
5504}
5505
5506static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5507{
5508 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5509 const QgsGeometry reoriented = fGeom.forceRHR();
5510 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5511}
5512
5513static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5514{
5515 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5516 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5517 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5518}
5519
5520static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5521{
5522 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5523 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5524 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5525}
5526
5527static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5528{
5529 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5531 if ( !pt && fGeom.isMultipart() )
5532 {
5534 {
5535 if ( collection->numGeometries() == 1 )
5536 {
5537 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5538 }
5539 }
5540 }
5541
5542 if ( !pt )
5543 {
5544 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5545 return QVariant();
5546 }
5547
5548 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5549 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5550 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5551 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5552
5553 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5554 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5555 return result;
5556}
5557
5558static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5559{
5560 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5561 if ( fGeom.type() != Qgis::GeometryType::Line )
5562 {
5563 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5564 return QVariant();
5565 }
5566
5567 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5568 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5569 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5570
5571 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5572 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5573 return result;
5574}
5575
5576static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5577{
5578 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5579 if ( fGeom.type() != Qgis::GeometryType::Line )
5580 {
5581 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5582 return QVariant();
5583 }
5584
5585 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5586
5587 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5588 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5589 return result;
5590}
5591
5592static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5593{
5594 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5595 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5596 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5597 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5598 if ( joinInt < 1 || joinInt > 3 )
5599 return QVariant();
5600 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5601
5602 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5603
5604 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5605 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5606 return result;
5607}
5608
5609static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5610{
5611 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5612 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5613 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5614
5615 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5616 if ( joinInt < 1 || joinInt > 3 )
5617 return QVariant();
5618 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5619
5620 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5621
5622 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5623 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5624 return result;
5625}
5626
5627static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5628{
5629 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5630 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5631 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5632
5633 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5634 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5635 return result;
5636}
5637
5638static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5639{
5640 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5641 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5642 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5643 fGeom.translate( dx, dy );
5644 return QVariant::fromValue( fGeom );
5645}
5646
5647static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5648{
5649 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5650 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5651 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent ) : QgsGeometry();
5652 const bool perPart = values.value( 3 ).toBool();
5653
5654 if ( center.isNull() && perPart && fGeom.isMultipart() )
5655 {
5656 // no explicit center, rotating per part
5657 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5658 // the result is equivalent to setting perPart as false anyway)
5659 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5660 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5661 {
5662 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5663 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5664 t.rotate( -rotation );
5665 t.translate( -partCenter.x(), -partCenter.y() );
5666 ( *it )->transform( t );
5667 }
5668 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5669 }
5670 else
5671 {
5672 QgsPointXY pt;
5673 if ( center.isEmpty() )
5674 {
5675 // if center wasn't specified, use bounding box centroid
5676 pt = fGeom.boundingBox().center();
5677 }
5679 {
5680 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5681 return QVariant();
5682 }
5683 else
5684 {
5686 }
5687
5688 fGeom.rotate( rotation, pt );
5689 return QVariant::fromValue( fGeom );
5690 }
5691}
5692
5693static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5694{
5695 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5696 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5697 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5698 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent ) : QgsGeometry();
5699
5700 QgsPointXY pt;
5701 if ( center.isNull() )
5702 {
5703 // if center wasn't specified, use bounding box centroid
5704 pt = fGeom.boundingBox().center();
5705 }
5707 {
5708 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5709 return QVariant();
5710 }
5711 else
5712 {
5713 pt = center.asPoint();
5714 }
5715
5716 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5717 t.scale( xScale, yScale );
5718 t.translate( -pt.x(), -pt.y() );
5719 fGeom.transform( t );
5720 return QVariant::fromValue( fGeom );
5721}
5722
5723static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5724{
5725 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5726 if ( fGeom.isNull() )
5727 {
5728 return QVariant();
5729 }
5730
5731 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5732 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5733
5734 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5735
5736 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5737 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5738
5739 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5740 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5741 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5742 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5743
5744 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5745 {
5746 fGeom.get()->addZValue( 0 );
5747 }
5748 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5749 {
5750 fGeom.get()->addMValue( 0 );
5751 }
5752
5753 QTransform transform;
5754 transform.translate( deltaX, deltaY );
5755 transform.rotate( rotationZ );
5756 transform.scale( scaleX, scaleY );
5757 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5758
5759 return QVariant::fromValue( fGeom );
5760}
5761
5762
5763static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5764{
5765 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5766 QgsGeometry geom = fGeom.centroid();
5767 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5768 return result;
5769}
5770static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5771{
5772 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5773 QgsGeometry geom = fGeom.pointOnSurface();
5774 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5775 return result;
5776}
5777
5778static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5779{
5780 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5781 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5782 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5783 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5784 return result;
5785}
5786
5787static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5788{
5789 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5790 QgsGeometry geom = fGeom.convexHull();
5791 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5792 return result;
5793}
5794
5795#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
5796static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5797{
5798 try
5799 {
5800 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5801 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5802 const bool allowHoles = values.value( 2 ).toBool();
5803 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5804 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5805 return result;
5806 }
5807 catch ( QgsCsException &cse )
5808 {
5809 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5810 return QVariant();
5811 }
5812}
5813#endif
5814
5815static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5816{
5817 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5818 int segments = 36;
5819 if ( values.length() == 2 )
5820 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5821 if ( segments < 0 )
5822 {
5823 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5824 return QVariant();
5825 }
5826
5827 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5828 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5829 return result;
5830}
5831
5832static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5833{
5834 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5836 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5837 return result;
5838}
5839
5840static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5841{
5842 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5843
5844 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5845 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5846 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5847
5848 double area, angle, width, height;
5849 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5850
5851 if ( geom.isNull() )
5852 {
5853 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5854 return QVariant();
5855 }
5856 return angle;
5857}
5858
5859static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5860{
5861 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5862 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5863 QgsGeometry geom = fGeom.difference( sGeom );
5864 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5865 return result;
5866}
5867
5868static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5869{
5870 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5871 return QVariant();
5872
5873 // two variants, one for geometry, one for string
5874
5875 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5876 if ( !fGeom.isNull() )
5877 {
5878 QVariant result;
5879 if ( !fGeom.isMultipart() )
5880 {
5881 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5882 if ( !curve )
5883 return QVariant();
5884
5885 QgsCurve *reversed = curve->reversed();
5886 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5887 }
5888 else
5889 {
5891 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5892 for ( int i = 0; i < collection->numGeometries(); ++i )
5893 {
5894 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5895 {
5896 reversed->addGeometry( curve->reversed() );
5897 }
5898 else
5899 {
5900 reversed->addGeometry( collection->geometryN( i )->clone() );
5901 }
5902 }
5903 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5904 }
5905 return result;
5906 }
5907
5908 //fall back to string variant
5909 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5910 std::reverse( string.begin(), string.end() );
5911 return string;
5912}
5913
5914static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5915{
5916 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5917 if ( fGeom.isNull() )
5918 return QVariant();
5919
5921 if ( !curvePolygon && fGeom.isMultipart() )
5922 {
5924 {
5925 if ( collection->numGeometries() == 1 )
5926 {
5927 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5928 }
5929 }
5930 }
5931
5932 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5933 return QVariant();
5934
5935 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5936 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5937 return result;
5938}
5939
5940static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5941{
5942 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5943 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5944 return QVariant( fGeom.distance( sGeom ) );
5945}
5946
5947static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5948{
5949 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5950 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5951
5952 double res = -1;
5953 if ( values.length() == 3 && values.at( 2 ).isValid() )
5954 {
5955 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5956 densify = std::clamp( densify, 0.0, 1.0 );
5957 res = g1.hausdorffDistanceDensify( g2, densify );
5958 }
5959 else
5960 {
5961 res = g1.hausdorffDistance( g2 );
5962 }
5963
5964 return res > -1 ? QVariant( res ) : QVariant();
5965}
5966
5967static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5968{
5969 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5970 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5971 QgsGeometry geom = fGeom.intersection( sGeom );
5972 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5973 return result;
5974}
5975static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5976{
5977 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5978 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5979 QgsGeometry geom = fGeom.symDifference( sGeom );
5980 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5981 return result;
5982}
5983static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5984{
5985 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5986 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5987 QgsGeometry geom = fGeom.combine( sGeom );
5988 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5989 return result;
5990}
5991
5992static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5993{
5994 if ( values.length() < 1 || values.length() > 2 )
5995 return QVariant();
5996
5997 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5998 int prec = 8;
5999 if ( values.length() == 2 )
6000 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6001 QString wkt = fGeom.asWkt( prec );
6002 return QVariant( wkt );
6003}
6004
6005static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6006{
6007 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6008 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
6009}
6010
6011static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6012{
6013 if ( values.length() != 2 )
6014 {
6015 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
6016 return QVariant();
6017 }
6018
6019 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6020 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6021
6022 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6023 if ( !pt1 && fGeom1.isMultipart() )
6024 {
6026 {
6027 if ( collection->numGeometries() == 1 )
6028 {
6029 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6030 }
6031 }
6032 }
6033
6034 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6035 if ( !pt2 && fGeom2.isMultipart() )
6036 {
6038 {
6039 if ( collection->numGeometries() == 1 )
6040 {
6041 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6042 }
6043 }
6044 }
6045
6046 if ( !pt1 || !pt2 )
6047 {
6048 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
6049 return QVariant();
6050 }
6051
6052 // Code from PostGIS
6053 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
6054 {
6055 if ( pt1->y() < pt2->y() )
6056 return 0.0;
6057 else if ( pt1->y() > pt2->y() )
6058 return M_PI;
6059 else
6060 return 0;
6061 }
6062
6063 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
6064 {
6065 if ( pt1->x() < pt2->x() )
6066 return M_PI_2;
6067 else if ( pt1->x() > pt2->x() )
6068 return M_PI + ( M_PI_2 );
6069 else
6070 return 0;
6071 }
6072
6073 if ( pt1->x() < pt2->x() )
6074 {
6075 if ( pt1->y() < pt2->y() )
6076 {
6077 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
6078 }
6079 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
6080 {
6081 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI_2 );
6082 }
6083 }
6084
6085 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
6086 {
6087 if ( pt1->y() > pt2->y() )
6088 {
6089 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI;
6090 }
6091 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
6092 {
6093 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI_2 ) );
6094 }
6095 }
6096}
6097
6098static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6099{
6100 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6101 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6102 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6103 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
6104
6105 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
6106 {
6107 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
6108 return QVariant();
6109 }
6110
6111 const QgsPointXY point1 = geom1.asPoint();
6112 const QgsPointXY point2 = geom2.asPoint();
6113 if ( point1.isEmpty() || point2.isEmpty() )
6114 {
6115 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
6116 return QVariant();
6117 }
6118
6120 if ( context )
6121 {
6122 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6123
6124 if ( !sourceCrs.isValid() )
6125 {
6126 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
6127 }
6128
6129 if ( ellipsoid.isEmpty() )
6130 {
6131 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
6132 }
6133 }
6134
6135 if ( !sourceCrs.isValid() )
6136 {
6137 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
6138 return QVariant();
6139 }
6140
6141 QgsDistanceArea da;
6142 da.setSourceCrs( sourceCrs, tContext );
6143 if ( !da.setEllipsoid( ellipsoid ) )
6144 {
6145 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
6146 return QVariant();
6147 }
6148
6149 try
6150 {
6151 const double bearing = da.bearing( point1, point2 );
6152 if ( std::isfinite( bearing ) )
6153 {
6154 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
6155 }
6156 }
6157 catch ( QgsCsException &cse )
6158 {
6159 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
6160 return QVariant();
6161 }
6162 return QVariant();
6163}
6164
6165static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6166{
6167 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6168
6170 {
6171 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
6172 return QVariant();
6173 }
6174
6175 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6176 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6177 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
6178
6179 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef() );
6180 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
6181
6182 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
6183}
6184
6185static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6186{
6187 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6188 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6189
6190 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6191 if ( !pt1 && fGeom1.isMultipart() )
6192 {
6194 {
6195 if ( collection->numGeometries() == 1 )
6196 {
6197 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6198 }
6199 }
6200 }
6201 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6202 if ( !pt2 && fGeom2.isMultipart() )
6203 {
6205 {
6206 if ( collection->numGeometries() == 1 )
6207 {
6208 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6209 }
6210 }
6211 }
6212
6213 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) || !pt1 || !pt2 )
6214 {
6215 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6216 return QVariant();
6217 }
6218
6219 return pt1->inclination( *pt2 );
6220}
6221
6222static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6223{
6224 if ( values.length() != 3 )
6225 return QVariant();
6226
6227 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6228 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6229 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6230
6231 QgsGeometry geom = fGeom.extrude( x, y );
6232
6233 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6234 return result;
6235}
6236
6237static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6238{
6239 if ( values.length() < 2 )
6240 return QVariant();
6241
6242 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6243
6244 if ( !fGeom.isMultipart() )
6245 return values.at( 0 );
6246
6247 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6248 QVariant cachedExpression;
6249 if ( ctx )
6250 cachedExpression = ctx->cachedValue( expString );
6251 QgsExpression expression;
6252
6253 if ( cachedExpression.isValid() )
6254 {
6255 expression = cachedExpression.value<QgsExpression>();
6256 }
6257 else
6258 expression = QgsExpression( expString );
6259
6260 bool asc = values.value( 2 ).toBool();
6261
6262 QgsExpressionContext *unconstedContext = nullptr;
6263 QgsFeature f;
6264 if ( ctx )
6265 {
6266 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6267 // so no reason to worry
6268 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6269 f = ctx->feature();
6270 }
6271 else
6272 {
6273 // If there's no context provided, create a fake one
6274 unconstedContext = new QgsExpressionContext();
6275 }
6276
6278 Q_ASSERT( collection ); // Should have failed the multipart check above
6279
6281 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6282 QgsExpressionSorter sorter( orderBy );
6283
6284 QList<QgsFeature> partFeatures;
6285 partFeatures.reserve( collection->partCount() );
6286 for ( int i = 0; i < collection->partCount(); ++i )
6287 {
6288 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6289 partFeatures << f;
6290 }
6291
6292 sorter.sortFeatures( partFeatures, unconstedContext );
6293
6295
6296 Q_ASSERT( orderedGeom );
6297
6298 while ( orderedGeom->partCount() )
6299 orderedGeom->removeGeometry( 0 );
6300
6301 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6302 {
6303 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6304 }
6305
6306 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6307
6308 if ( !ctx )
6309 delete unconstedContext;
6310
6311 return result;
6312}
6313
6314static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6315{
6316 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6317 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6318
6319 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6320
6321 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6322 return result;
6323}
6324
6325static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6326{
6327 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6328 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6329
6330 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6331
6332 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6333 return result;
6334}
6335
6336static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6337{
6338 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6339 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6340
6341 QgsGeometry geom = lineGeom.interpolate( distance );
6342
6343 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6344 return result;
6345}
6346
6347static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6348{
6349 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6350 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6351 const bool use3DDistance = values.at( 2 ).toBool();
6352
6353 double x, y, z, distance;
6354
6356 if ( !line )
6357 {
6358 return QVariant();
6359 }
6360
6361 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6362 {
6363 QgsPoint point( x, y );
6364 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6365 {
6366 point.addZValue( z );
6367 }
6368 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6369 }
6370
6371 return QVariant();
6372}
6373
6374static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6375{
6376 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6377 if ( lineGeom.type() != Qgis::GeometryType::Line )
6378 {
6379 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6380 return QVariant();
6381 }
6382
6383 const QgsCurve *curve = nullptr;
6384 if ( !lineGeom.isMultipart() )
6385 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6386 else
6387 {
6389 {
6390 if ( collection->numGeometries() > 0 )
6391 {
6392 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6393 }
6394 }
6395 }
6396 if ( !curve )
6397 return QVariant();
6398
6399 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6400 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6401
6402 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6403 QgsGeometry result( std::move( substring ) );
6404 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6405}
6406
6407static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6408{
6409 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6410 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6411
6412 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6413}
6414
6415static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6416{
6417 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6418 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6419 if ( vertex < 0 )
6420 {
6421 //negative idx
6422 int count = geom.constGet()->nCoordinates();
6423 vertex = count + vertex;
6424 }
6425
6426 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6427}
6428
6429static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6430{
6431 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6432 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6433 if ( vertex < 0 )
6434 {
6435 //negative idx
6436 int count = geom.constGet()->nCoordinates();
6437 vertex = count + vertex;
6438 }
6439
6440 return geom.distanceToVertex( vertex );
6441}
6442
6443static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6444{
6445 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6446 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6447
6448 double distance = lineGeom.lineLocatePoint( pointGeom );
6449
6450 return distance >= 0 ? distance : QVariant();
6451}
6452
6453static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6454{
6455 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6456 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6457 const bool use3DDistance = values.at( 2 ).toBool();
6458
6459 double x, y, z, distance;
6460
6462 if ( !line )
6463 {
6464 return QVariant();
6465 }
6466
6467 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6468 return found ? distance : QVariant();
6469}
6470
6471static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6472{
6473 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6474 {
6475 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6476 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6477 }
6478
6479 if ( values.length() >= 1 )
6480 {
6481 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6482 return QVariant( qlonglong( std::round( number ) ) );
6483 }
6484
6485 return QVariant();
6486}
6487
6488static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6489{
6490 Q_UNUSED( values )
6491 Q_UNUSED( parent )
6492 return M_PI;
6493}
6494
6495static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6496{
6497 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6498 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6499 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6500 if ( places < 0 )
6501 {
6502 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6503 return QVariant();
6504 }
6505
6506 const bool omitGroupSeparator = values.value( 3 ).toBool();
6507 const bool trimTrailingZeros = values.value( 4 ).toBool();
6508
6509 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6510 if ( !omitGroupSeparator )
6511 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6512 else
6513 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6514
6515 QString res = locale.toString( value, 'f', places );
6516
6517 if ( trimTrailingZeros )
6518 {
6519 const QChar decimal = locale.decimalPoint().at( 0 );
6520 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6521
6522 if ( res.contains( decimal ) )
6523 {
6524 int trimPoint = res.length() - 1;
6525
6526 while ( res.at( trimPoint ) == zeroDigit )
6527 trimPoint--;
6528
6529 if ( res.at( trimPoint ) == decimal )
6530 trimPoint--;
6531
6532 res.truncate( trimPoint + 1 );
6533 }
6534 }
6535
6536 return res;
6537}
6538
6539static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6540{
6541 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6542 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6543 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6544
6545 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6546 if ( format.indexOf( "Z" ) > 0 )
6547 datetime = datetime.toUTC();
6548
6549 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6550 return locale.toString( datetime, format );
6551}
6552
6553static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6554{
6555 const QVariant variant = values.at( 0 );
6556 bool isQColor;
6557 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6558 if ( !color.isValid() )
6559 return QVariant();
6560
6561 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6562 if ( color.spec() == QColor::Spec::Cmyk )
6563 {
6564 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() )
6565 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6566 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6567 }
6568 else
6569 {
6570 const float avg = ( color.redF() + color.greenF() + color.blueF() )
6571 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6572 color.setRgbF( avg, avg, avg, alpha );
6573 }
6574
6575 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6576}
6577
6578static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6579{
6580 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6581 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6582 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6583 if ( ratio > 1 )
6584 {
6585 ratio = 1;
6586 }
6587 else if ( ratio < 0 )
6588 {
6589 ratio = 0;
6590 }
6591
6592 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6593 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6594 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6595 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6596
6597 QColor newColor( red, green, blue, alpha );
6598
6599 return QgsSymbolLayerUtils::encodeColor( newColor );
6600}
6601
6602static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6603{
6604 const QVariant variant1 = values.at( 0 );
6605 const QVariant variant2 = values.at( 1 );
6606
6607 if ( variant1.userType() != variant2.userType() )
6608 {
6609 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6610 return QVariant();
6611 }
6612
6613 bool isQColor;
6614 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6615 if ( !color1.isValid() )
6616 return QVariant();
6617
6618 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6619 if ( !color2.isValid() )
6620 return QVariant();
6621
6622 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6623 {
6624 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6625 return QVariant();
6626 }
6627
6628 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6629
6630 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6631 // NOLINTBEGIN(bugprone-narrowing-conversions)
6632
6633 QColor newColor;
6634 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6635 if ( color1.spec() == QColor::Spec::Cmyk )
6636 {
6637 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6638 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6639 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6640 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6641 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6642 }
6643 else
6644 {
6645 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6646 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6647 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6648 newColor = QColor::fromRgbF( red, green, blue, alpha );
6649 }
6650
6651 // NOLINTEND(bugprone-narrowing-conversions)
6652
6653 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6654}
6655
6656static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6657{
6658 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6659 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6660 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6661 QColor color = QColor( red, green, blue );
6662 if ( !color.isValid() )
6663 {
6664 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6665 color = QColor( 0, 0, 0 );
6666 }
6667
6668 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6669}
6670
6671static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6672{
6673 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6674 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6675 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6676 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6677 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6678 if ( !color.isValid() )
6679 {
6680 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6681 return QVariant();
6682 }
6683
6684 return color;
6685}
6686
6687static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6688{
6689 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6690 QVariant value = node->eval( parent, context );
6691 if ( parent->hasEvalError() )
6692 {
6693 parent->setEvalErrorString( QString() );
6694 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6696 value = node->eval( parent, context );
6698 }
6699 return value;
6700}
6701
6702static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6703{
6704 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6706 QVariant value = node->eval( parent, context );
6708 if ( value.toBool() )
6709 {
6710 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6712 value = node->eval( parent, context );
6714 }
6715 else
6716 {
6717 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6719 value = node->eval( parent, context );
6721 }
6722 return value;
6723}
6724
6725static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6726{
6727 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6728 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6729 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6730 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6731 QColor color = QColor( red, green, blue, alpha );
6732 if ( !color.isValid() )
6733 {
6734 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6735 color = QColor( 0, 0, 0 );
6736 }
6737 return QgsSymbolLayerUtils::encodeColor( color );
6738}
6739
6740QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6741{
6742 QgsGradientColorRamp expRamp;
6743 const QgsColorRamp *ramp = nullptr;
6744 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6745 {
6746 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6747 ramp = &expRamp;
6748 }
6749 else
6750 {
6751 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6752 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6753 if ( !ramp )
6755 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6756 return QVariant();
6757 }
6758 }
6759
6760 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6761 QColor color = ramp->color( value );
6762 return color;
6763}
6764
6765QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6766{
6767 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6768 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6769}
6770
6771static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6772{
6773 // Hue ranges from 0 - 360
6774 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6775 // Saturation ranges from 0 - 100
6776 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6777 // Lightness ranges from 0 - 100
6778 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6779
6780 QColor color = QColor::fromHslF( hue, saturation, lightness );
6781
6782 if ( !color.isValid() )
6783 {
6784 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6785 color = QColor( 0, 0, 0 );
6786 }
6787
6788 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6789}
6790
6791static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6792{
6793 // Hue ranges from 0 - 360
6794 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6795 // Saturation ranges from 0 - 100
6796 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6797 // Lightness ranges from 0 - 100
6798 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6799 // Alpha ranges from 0 - 255
6800 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6801
6802 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6803 if ( !color.isValid() )
6804 {
6805 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6806 color = QColor( 0, 0, 0 );
6807 }
6808 return QgsSymbolLayerUtils::encodeColor( color );
6809}
6810
6811static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6812{
6813 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6814 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6815 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6816 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6817
6818 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6819 if ( !color.isValid() )
6820 {
6821 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6822 return QVariant();
6823 }
6824
6825 return color;
6826}
6827
6828static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6829{
6830 // Hue ranges from 0 - 360
6831 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6832 // Saturation ranges from 0 - 100
6833 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6834 // Value ranges from 0 - 100
6835 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6836
6837 QColor color = QColor::fromHsvF( hue, saturation, value );
6838
6839 if ( !color.isValid() )
6840 {
6841 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6842 color = QColor( 0, 0, 0 );
6843 }
6844
6845 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6846}
6847
6848static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6849{
6850 // Hue ranges from 0 - 360
6851 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6852 // Saturation ranges from 0 - 100
6853 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6854 // Value ranges from 0 - 100
6855 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6856 // Alpha ranges from 0 - 255
6857 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6858
6859 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6860 if ( !color.isValid() )
6861 {
6862 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6863 color = QColor( 0, 0, 0 );
6864 }
6865 return QgsSymbolLayerUtils::encodeColor( color );
6866}
6867
6868static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6869{
6870 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6871 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6872 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6873 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6874 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6875
6876 if ( !color.isValid() )
6877 {
6878 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6879 return QVariant();
6880 }
6881
6882 return color;
6883}
6884
6885static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6886{
6887 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6888 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6889 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6890 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6891 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6892
6893 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6894 if ( !color.isValid() )
6895 {
6896 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6897 return QVariant();
6898 }
6899
6900 return color;
6901}
6902
6903static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6904{
6905 // Cyan ranges from 0 - 100
6906 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6907 // Magenta ranges from 0 - 100
6908 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6909 // Yellow ranges from 0 - 100
6910 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6911 // Black ranges from 0 - 100
6912 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6913
6914 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6915
6916 if ( !color.isValid() )
6917 {
6918 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6919 color = QColor( 0, 0, 0 );
6920 }
6921
6922 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6923}
6924
6925static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6926{
6927 // Cyan ranges from 0 - 100
6928 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6929 // Magenta ranges from 0 - 100
6930 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6931 // Yellow ranges from 0 - 100
6932 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6933 // Black ranges from 0 - 100
6934 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6935 // Alpha ranges from 0 - 255
6936 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6937
6938 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6939 if ( !color.isValid() )
6940 {
6941 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6942 color = QColor( 0, 0, 0 );
6943 }
6944 return QgsSymbolLayerUtils::encodeColor( color );
6945}
6946
6947static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6948{
6949 const QVariant variant = values.at( 0 );
6950 bool isQColor;
6951 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6952 if ( !color.isValid() )
6953 return QVariant();
6954
6955 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6956 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6957 return color.red();
6958 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6959 return color.green();
6960 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6961 return color.blue();
6962 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6963 return color.alpha();
6964 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6965 return static_cast< double >( color.hsvHueF() * 360 );
6966 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6967 return static_cast< double >( color.hsvSaturationF() * 100 );
6968 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6969 return static_cast< double >( color.valueF() * 100 );
6970 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6971 return static_cast< double >( color.hslHueF() * 360 );
6972 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6973 return static_cast< double >( color.hslSaturationF() * 100 );
6974 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6975 return static_cast< double >( color.lightnessF() * 100 );
6976 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6977 return static_cast< double >( color.cyanF() * 100 );
6978 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6979 return static_cast< double >( color.magentaF() * 100 );
6980 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6981 return static_cast< double >( color.yellowF() * 100 );
6982 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6983 return static_cast< double >( color.blackF() * 100 );
6984
6985 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6986 return QVariant();
6987}
6988
6989static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6990{
6991 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6992 if ( map.empty() )
6993 {
6994 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6995 return QVariant();
6996 }
6997
6998 QList< QColor > colors;
7000 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7001 {
7002 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
7003 if ( !colors.last().isValid() )
7004 {
7005 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
7006 return QVariant();
7007 }
7008
7009 double step = it.key().toDouble();
7010 if ( it == map.constBegin() )
7011 {
7012 if ( step != 0.0 )
7013 stops << QgsGradientStop( step, colors.last() );
7014 }
7015 else if ( it == map.constEnd() )
7016 {
7017 if ( step != 1.0 )
7018 stops << QgsGradientStop( step, colors.last() );
7019 }
7020 else
7021 {
7022 stops << QgsGradientStop( step, colors.last() );
7023 }
7024 }
7025 bool discrete = values.at( 1 ).toBool();
7026
7027 if ( colors.empty() )
7028 return QVariant();
7029
7030 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
7031}
7032
7033static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7034{
7035 const QVariant variant = values.at( 0 );
7036 bool isQColor;
7037 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7038 if ( !color.isValid() )
7039 return QVariant();
7040
7041 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7042 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7043 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
7044 color.setRed( std::clamp( value, 0, 255 ) );
7045 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
7046 color.setGreen( std::clamp( value, 0, 255 ) );
7047 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
7048 color.setBlue( std::clamp( value, 0, 255 ) );
7049 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
7050 color.setAlpha( std::clamp( value, 0, 255 ) );
7051 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7052 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
7053 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7054 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
7055 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7056 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7057 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7058 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
7059 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7060 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
7061 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7062 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7063 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7064 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
7065 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7066 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
7067 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7068 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
7069 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7070 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7071 else
7072 {
7073 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7074 return QVariant();
7075 }
7076 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7077}
7078
7079static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7080{
7081 const QVariant variant = values.at( 0 );
7082 bool isQColor;
7083 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7084 if ( !color.isValid() )
7085 return QVariant();
7086
7087 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7088
7089 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7090}
7091
7092static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7093{
7094 const QVariant variant = values.at( 0 );
7095 bool isQColor;
7096 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7097 if ( !color.isValid() )
7098 return QVariant();
7099
7100 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7101
7102 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7103}
7104
7105static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7106{
7107 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7108 QgsGeometry geom = feat.geometry();
7109 if ( !geom.isNull() )
7110 return QVariant::fromValue( geom );
7111 return QVariant();
7112}
7113
7114static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7115{
7116 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7117 if ( !feat.isValid() )
7118 return QVariant();
7119 return feat.id();
7120}
7121
7122static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7123{
7124 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
7125 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
7126 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
7127
7128 if ( !sCrs.isValid() )
7129 return QVariant::fromValue( fGeom );
7130
7131 if ( !dCrs.isValid() )
7132 return QVariant::fromValue( fGeom );
7133
7135 if ( context )
7136 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
7137 QgsCoordinateTransform t( sCrs, dCrs, tContext );
7138 try
7139 {
7141 return QVariant::fromValue( fGeom );
7142 }
7143 catch ( QgsCsException &cse )
7144 {
7145 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
7146 return QVariant();
7147 }
7148 return QVariant();
7149}
7150
7151
7152static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7153{
7154 bool foundLayer = false;
7155 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7156
7157 //no layer found
7158 if ( !featureSource || !foundLayer )
7159 {
7160 return QVariant();
7161 }
7162
7163 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
7164
7166 req.setFilterFid( fid );
7167 req.setTimeout( 10000 );
7168 req.setRequestMayBeNested( true );
7169 if ( context )
7170 req.setFeedback( context->feedback() );
7171 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7172
7173 QgsFeature fet;
7174 QVariant result;
7175 if ( fIt.nextFeature( fet ) )
7176 result = QVariant::fromValue( fet );
7177
7178 return result;
7179}
7180
7181static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7182{
7183 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
7184 bool foundLayer = false;
7185 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7186
7187 //no layer found
7188 if ( !featureSource || !foundLayer )
7189 {
7190 return QVariant();
7191 }
7193 QString cacheValueKey;
7194 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7195 {
7196 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7197
7198 QMap<QString, QVariant>::const_iterator i = attributeMap.constBegin();
7199 QString filterString;
7200 for ( ; i != attributeMap.constEnd(); ++i )
7201 {
7202 if ( !filterString.isEmpty() )
7203 {
7204 filterString.append( " AND " );
7205 }
7206 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7207 }
7208 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7209 if ( context && context->hasCachedValue( cacheValueKey ) )
7210 {
7211 return context->cachedValue( cacheValueKey );
7212 }
7213 req.setFilterExpression( filterString );
7214 }
7215 else
7216 {
7217 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7218 int attributeId = featureSource->fields().lookupField( attribute );
7219 if ( attributeId == -1 )
7220 {
7221 return QVariant();
7222 }
7223
7224 const QVariant &attVal = values.at( 2 );
7225
7226 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7227 if ( context && context->hasCachedValue( cacheValueKey ) )
7228 {
7229 return context->cachedValue( cacheValueKey );
7230 }
7231
7233 }
7234 req.setLimit( 1 );
7235 req.setTimeout( 10000 );
7236 req.setRequestMayBeNested( true );
7237 if ( context )
7238 req.setFeedback( context->feedback() );
7239 if ( !parent->needsGeometry() )
7240 {
7242 }
7243 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7244
7245 QgsFeature fet;
7246 QVariant res;
7247 if ( fIt.nextFeature( fet ) )
7248 {
7249 res = QVariant::fromValue( fet );
7250 }
7251
7252 if ( context )
7253 context->setCachedValue( cacheValueKey, res );
7254 return res;
7255}
7256
7257static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7258{
7259 QVariant result;
7260 QString fieldName;
7261
7262 if ( context )
7263 {
7264 if ( !values.isEmpty() )
7265 {
7266 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7267 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7268 fieldName = col->name();
7269 else if ( values.size() == 2 )
7270 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7271 }
7272
7273 QVariant value = values.at( 0 );
7274
7275 const QgsFields fields = context->fields();
7276 int fieldIndex = fields.lookupField( fieldName );
7277
7278 if ( fieldIndex == -1 )
7279 {
7280 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7281 }
7282 else
7283 {
7284 // TODO this function is NOT thread safe
7286 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7288
7289 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7290 if ( context->hasCachedValue( cacheValueKey ) )
7291 {
7292 return context->cachedValue( cacheValueKey );
7293 }
7294
7295 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7297
7298 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7299
7300 QVariant cache;
7301 if ( !context->hasCachedValue( cacheKey ) )
7302 {
7303 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7304 context->setCachedValue( cacheKey, cache );
7305 }
7306 else
7307 cache = context->cachedValue( cacheKey );
7308
7309 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7310
7311 context->setCachedValue( cacheValueKey, result );
7312 }
7313 }
7314 else
7315 {
7316 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7317 }
7318
7319 return result;
7320}
7321
7322static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7323{
7324 const QVariant data = values.at( 0 );
7325 const QMimeDatabase db;
7326 return db.mimeTypeForData( data.toByteArray() ).name();
7327}
7328
7329static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7330{
7331 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7332
7333 bool foundLayer = false;
7334 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7335 values.at( 0 ),
7336 context,
7337 parent,
7338 [layerProperty]( QgsMapLayer *layer ) -> QVariant {
7339 if ( !layer )
7340 return QVariant();
7341
7342 // here, we always prefer the layer metadata values over the older server-specific published values
7343 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7344 return layer->name();
7345 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7346 return layer->id();
7347 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7348 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7349 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7350 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7351 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7352 {
7353 QStringList keywords;
7354 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7355 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7356 {
7357 keywords.append( it.value() );
7358 }
7359 if ( !keywords.isEmpty() )
7360 return keywords;
7361 return layer->serverProperties()->keywordList();
7362 }
7363 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7364 return layer->serverProperties()->dataUrl();
7365 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7366 {
7367 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7368 }
7369 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7370 return layer->serverProperties()->attributionUrl();
7371 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7372 return layer->publicSource();
7373 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7374 return layer->minimumScale();
7375 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7376 return layer->maximumScale();
7377 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7378 return layer->isEditable();
7379 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7380 return layer->crs().authid();
7381 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7382 return layer->crs().toProj();
7383 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7384 return layer->crs().description();
7385 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7386 return layer->crs().ellipsoidAcronym();
7387 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7388 {
7389 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7390 QVariant result = QVariant::fromValue( extentGeom );
7391 return result;
7392 }
7393 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7394 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7395 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7396 {
7397 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7398 return decodedUri.value( u"path"_s );
7399 }
7400 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7401 {
7402 switch ( layer->type() )
7403 {
7405 return QCoreApplication::translate( "expressions", "Vector" );
7407 return QCoreApplication::translate( "expressions", "Raster" );
7409 return QCoreApplication::translate( "expressions", "Mesh" );
7411 return QCoreApplication::translate( "expressions", "Vector Tile" );
7413 return QCoreApplication::translate( "expressions", "Plugin" );
7415 return QCoreApplication::translate( "expressions", "Annotation" );
7417 return QCoreApplication::translate( "expressions", "Point Cloud" );
7419 return QCoreApplication::translate( "expressions", "Group" );
7421 return QCoreApplication::translate( "expressions", "Tiled Scene" );
7422 }
7423 }
7424 else
7425 {
7426 //vector layer methods
7427 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7428 if ( vLayer )
7429 {
7430 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7431 return vLayer->storageType();
7432 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7434 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7435 return QVariant::fromValue( vLayer->featureCount() );
7436 }
7437 }
7438
7439 return QVariant();
7440 },
7441 foundLayer
7442 );
7443
7444 if ( !foundLayer )
7445 return QVariant();
7446 else
7447 return res;
7448}
7449
7450static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7451{
7452 const QString uriPart = values.at( 1 ).toString();
7453
7454 bool foundLayer = false;
7455
7456 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7457 values.at( 0 ),
7458 context,
7459 parent,
7460 [parent, uriPart]( QgsMapLayer *layer ) -> QVariant {
7461 if ( !layer->dataProvider() )
7462 {
7463 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7464 return QVariant();
7465 }
7466
7467 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7468
7469 if ( !uriPart.isNull() )
7470 {
7471 return decodedUri.value( uriPart );
7472 }
7473 else
7474 {
7475 return decodedUri;
7476 }
7477 },
7478 foundLayer
7479 );
7480
7481 if ( !foundLayer )
7482 {
7483 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7484 return QVariant();
7485 }
7486 else
7487 {
7488 return res;
7489 }
7490}
7491
7492static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7493{
7494 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7495 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7496
7497 bool foundLayer = false;
7498 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7499 values.at( 0 ),
7500 context,
7501 parent,
7502 [parent, band, layerProperty]( QgsMapLayer *layer ) -> QVariant {
7503 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7504 if ( !rl )
7505 return QVariant();
7506
7507 if ( band < 1 || band > rl->bandCount() )
7508 {
7509 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7510 return QVariant();
7511 }
7512
7514
7515 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7517 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7519 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7521 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7523 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7525 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7527 else
7528 {
7529 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7530 return QVariant();
7531 }
7532
7533 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7534 switch ( stat )
7535 {
7537 return stats.mean;
7539 return stats.stdDev;
7541 return stats.minimumValue;
7543 return stats.maximumValue;
7545 return stats.range;
7547 return stats.sum;
7548 default:
7549 break;
7550 }
7551 return QVariant();
7552 },
7553 foundLayer
7554 );
7555
7556 if ( !foundLayer )
7557 {
7558#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7559 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7560#endif
7561 return QVariant();
7562 }
7563 else
7564 {
7565 return res;
7566 }
7567}
7568
7569static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7570{
7571 return values;
7572}
7573
7574static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7575{
7576 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7577 bool ascending = values.value( 1 ).toBool();
7578 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7579 return list;
7580}
7581
7582static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7583{
7584 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7585}
7586
7587static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7588{
7589 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7590}
7591
7592static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7593{
7594 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7595}
7596
7597static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7598{
7599 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7600 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7601 int match = 0;
7602 for ( const auto &item : listB )
7603 {
7604 if ( listA.contains( item ) )
7605 match++;
7606 }
7607
7608 return QVariant( match == listB.count() );
7609}
7610
7611static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7612{
7613 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7614}
7615
7616static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7617{
7618 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7619 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7620 if ( pos < list.length() && pos >= 0 )
7621 return list.at( pos );
7622 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7623 return list.at( list.length() + pos );
7624 return QVariant();
7625}
7626
7627static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7628{
7629 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7630 return list.value( 0 );
7631}
7632
7633static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7634{
7635 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7636 return list.value( list.size() - 1 );
7637}
7638
7639static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7640{
7641 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7642 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7643}
7644
7645static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7646{
7647 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7648 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7649}
7650
7651static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7652{
7653 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7654 int i = 0;
7655 double total = 0.0;
7656 for ( const QVariant &item : list )
7657 {
7658 switch ( item.userType() )
7659 {
7660 case QMetaType::Int:
7661 case QMetaType::UInt:
7662 case QMetaType::LongLong:
7663 case QMetaType::ULongLong:
7664 case QMetaType::Float:
7665 case QMetaType::Double:
7666 total += item.toDouble();
7667 ++i;
7668 break;
7669 }
7670 }
7671 return i == 0 ? QVariant() : total / i;
7672}
7673
7674static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7675{
7676 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7677 QVariantList numbers;
7678 for ( const auto &item : list )
7679 {
7680 switch ( item.userType() )
7681 {
7682 case QMetaType::Int:
7683 case QMetaType::UInt:
7684 case QMetaType::LongLong:
7685 case QMetaType::ULongLong:
7686 case QMetaType::Float:
7687 case QMetaType::Double:
7688 numbers.append( item );
7689 break;
7690 }
7691 }
7692 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7693 const int count = numbers.count();
7694 if ( count == 0 )
7695 {
7696 return QVariant();
7697 }
7698 else if ( count % 2 )
7699 {
7700 return numbers.at( count / 2 );
7701 }
7702 else
7703 {
7704 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7705 }
7706}
7707
7708static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7709{
7710 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7711 int i = 0;
7712 double total = 0.0;
7713 for ( const QVariant &item : list )
7714 {
7715 switch ( item.userType() )
7716 {
7717 case QMetaType::Int:
7718 case QMetaType::UInt:
7719 case QMetaType::LongLong:
7720 case QMetaType::ULongLong:
7721 case QMetaType::Float:
7722 case QMetaType::Double:
7723 total += item.toDouble();
7724 ++i;
7725 break;
7726 }
7727 }
7728 return i == 0 ? QVariant() : total;
7729}
7730
7731static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7732{
7733 QVariant result = value;
7734 ( void ) result.convert( static_cast<int>( type ) );
7735 return result;
7736}
7737
7738static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7739{
7740 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7741 QHash< QVariant, int > hash;
7742 for ( const auto &item : list )
7743 {
7744 ++hash[item];
7745 }
7746 const QList< int > occurrences = hash.values();
7747 if ( occurrences.empty() )
7748 return QVariantList();
7749
7750 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7751
7752 const QString option = values.at( 1 ).toString();
7753 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7754 {
7755 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7756 }
7757 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7758 {
7759 if ( hash.isEmpty() )
7760 return QVariant();
7761
7762 return QVariant( hash.key( maxValue ) );
7763 }
7764 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7765 {
7766 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7767 }
7768 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7769 {
7770 if ( maxValue * 2 <= list.size() )
7771 return QVariant();
7772
7773 return QVariant( hash.key( maxValue ) );
7774 }
7775 else
7776 {
7777 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7778 return QVariant();
7779 }
7780}
7781
7782static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7783{
7784 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7785 QHash< QVariant, int > hash;
7786 for ( const auto &item : list )
7787 {
7788 ++hash[item];
7789 }
7790 const QList< int > occurrences = hash.values();
7791 if ( occurrences.empty() )
7792 return QVariantList();
7793
7794 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7795
7796 const QString option = values.at( 1 ).toString();
7797 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7798 {
7799 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7800 }
7801 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7802 {
7803 if ( hash.isEmpty() )
7804 return QVariant();
7805
7806 return QVariant( hash.key( minValue ) );
7807 }
7808 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7809 {
7810 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7811 }
7812 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7813 {
7814 if ( hash.isEmpty() )
7815 return QVariant();
7816
7817 // Remove the majority, all others are minority
7818 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7819 if ( maxValue * 2 > list.size() )
7820 hash.remove( hash.key( maxValue ) );
7821
7822 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7823 }
7824 else
7825 {
7826 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7827 return QVariant();
7828 }
7829}
7830
7831static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7832{
7833 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7834 list.append( values.at( 1 ) );
7835 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7836}
7837
7838static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7839{
7840 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7841 list.prepend( values.at( 1 ) );
7842 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7843}
7844
7845static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7846{
7847 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7848 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7849 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7850}
7851
7852static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7853{
7854 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7855 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7856 if ( position < 0 )
7857 position = position + list.length();
7858 if ( position >= 0 && position < list.length() )
7859 list.removeAt( position );
7860 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7861}
7862
7863static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7864{
7865 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7866 return QVariant();
7867
7868 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7869
7870 const QVariant toRemove = values.at( 1 );
7871 if ( QgsVariantUtils::isNull( toRemove ) )
7872 {
7873 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant &element ) { return QgsVariantUtils::isNull( element ); } ), list.end() );
7874 }
7875 else
7876 {
7877 list.removeAll( toRemove );
7878 }
7879 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7880}
7881
7882static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7883{
7884 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7885 {
7886 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7887
7888 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7889 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7890 {
7891 int index = list.indexOf( it.key() );
7892 while ( index >= 0 )
7893 {
7894 list.replace( index, it.value() );
7895 index = list.indexOf( it.key() );
7896 }
7897 }
7898
7899 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7900 }
7901 else if ( values.count() == 3 )
7902 {
7903 QVariantList before;
7904 QVariantList after;
7905 bool isSingleReplacement = false;
7906
7907 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7908 {
7909 before = QVariantList() << values.at( 1 );
7910 }
7911 else
7912 {
7913 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7914 }
7915
7916 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7917 {
7918 after = QVariantList() << values.at( 2 );
7919 isSingleReplacement = true;
7920 }
7921 else
7922 {
7923 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7924 }
7925
7926 if ( !isSingleReplacement && before.length() != after.length() )
7927 {
7928 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7929 return QVariant();
7930 }
7931
7932 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7933 for ( int i = 0; i < before.length(); i++ )
7934 {
7935 int index = list.indexOf( before.at( i ) );
7936 while ( index >= 0 )
7937 {
7938 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7939 index = list.indexOf( before.at( i ) );
7940 }
7941 }
7942
7943 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7944 }
7945 else
7946 {
7947 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7948 return QVariant();
7949 }
7950}
7951
7952static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7953{
7954 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7955 QVariantList list_new;
7956
7957 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7958 {
7959 while ( list.removeOne( cur ) )
7960 {
7961 list_new.append( cur );
7962 }
7963 }
7964
7965 list_new.append( list );
7966
7967 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7968}
7969
7970static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7971{
7972 QVariantList list;
7973 for ( const QVariant &cur : values )
7974 {
7975 list += QgsExpressionUtils::getListValue( cur, parent );
7976 }
7977 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7978}
7979
7980static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7981{
7982 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7983 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7984 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7985 int slice_length = 0;
7986 // negative positions means positions taken relative to the end of the array
7987 if ( start_pos < 0 )
7988 {
7989 start_pos = list.length() + start_pos;
7990 }
7991 if ( end_pos >= 0 )
7992 {
7993 slice_length = end_pos - start_pos + 1;
7994 }
7995 else
7996 {
7997 slice_length = list.length() + end_pos - start_pos + 1;
7998 }
7999 //avoid negative lengths in QList.mid function
8000 if ( slice_length < 0 )
8001 {
8002 slice_length = 0;
8003 }
8004 list = list.mid( start_pos, slice_length );
8005 return list;
8006}
8007
8008static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8009{
8010 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8011 std::reverse( list.begin(), list.end() );
8012 return list;
8013}
8014
8015static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8016{
8017 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8018 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
8019 for ( const QVariant &cur : array2 )
8020 {
8021 if ( array1.contains( cur ) )
8022 return QVariant( true );
8023 }
8024 return QVariant( false );
8025}
8026
8027static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8028{
8029 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8030
8031 QVariantList distinct;
8032
8033 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8034 {
8035 if ( !distinct.contains( *it ) )
8036 {
8037 distinct += ( *it );
8038 }
8039 }
8040
8041 return distinct;
8042}
8043
8044static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8045{
8046 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8047 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8048 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8049
8050 QString str;
8051
8052 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8053 {
8054 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
8055 if ( it != ( array.constEnd() - 1 ) )
8056 {
8057 str += delimiter;
8058 }
8059 }
8060
8061 return QVariant( str );
8062}
8063
8064static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8065{
8066 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8067 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8068 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8069
8070 QStringList list = str.split( delimiter );
8071 QVariantList array;
8072
8073 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
8074 {
8075 array += ( !( *it ).isEmpty() ) ? *it : empty;
8076 }
8077
8078 return array;
8079}
8080
8081static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8082{
8083 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8084 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
8085 if ( document.isNull() )
8086 return QVariant();
8087
8088 return document.toVariant();
8089}
8090
8091static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8092{
8093 Q_UNUSED( parent )
8094 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
8095 return QString( document.toJson( QJsonDocument::Compact ) );
8096}
8097
8098static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8099{
8100 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8101 if ( str.isEmpty() )
8102 return QVariantMap();
8103 str = str.trimmed();
8104
8105 return QgsHstoreUtils::parse( str );
8106}
8107
8108static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8109{
8110 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8111 return QgsHstoreUtils::build( map );
8112}
8113
8114static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8115{
8116 QVariantMap result;
8117 for ( int i = 0; i + 1 < values.length(); i += 2 )
8118 {
8119 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
8120 }
8121 return result;
8122}
8123
8124static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8125{
8126 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8127 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8128 QVariantMap resultMap;
8129
8130 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8131 {
8132 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
8133 }
8134
8135 return resultMap;
8136}
8137
8138static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8139{
8140 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
8141}
8142
8143static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8144{
8145 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
8146}
8147
8148static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8149{
8150 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8151 map.remove( values.at( 1 ).toString() );
8152 return map;
8153}
8154
8155static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8156{
8157 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8158 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
8159 return map;
8160}
8161
8162static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8163{
8164 QVariantMap result;
8165 for ( const QVariant &cur : values )
8166 {
8167 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
8168 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
8169 result.insert( it.key(), it.value() );
8170 }
8171 return result;
8172}
8173
8174static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8175{
8176 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
8177}
8178
8179static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8180{
8181 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
8182}
8183
8184static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8185{
8186 const QString envVarName = values.at( 0 ).toString();
8187 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
8188 return QVariant();
8189
8190 return QProcessEnvironment::systemEnvironment().value( envVarName );
8191}
8192
8193static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8194{
8195 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8196 if ( parent->hasEvalError() )
8197 {
8198 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "base_file_name"_L1 ) );
8199 return QVariant();
8200 }
8201 return QFileInfo( file ).completeBaseName();
8202}
8203
8204static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8205{
8206 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8207 if ( parent->hasEvalError() )
8208 {
8209 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_suffix"_L1 ) );
8210 return QVariant();
8211 }
8212 return QFileInfo( file ).completeSuffix();
8213}
8214
8215static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8216{
8217 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8218 if ( parent->hasEvalError() )
8219 {
8220 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8221 return QVariant();
8222 }
8223 return QFileInfo::exists( file );
8224}
8225
8226static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8227{
8228 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8229 if ( parent->hasEvalError() )
8230 {
8231 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_name"_L1 ) );
8232 return QVariant();
8233 }
8234 return QFileInfo( file ).fileName();
8235}
8236
8237static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8238{
8239 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8240 if ( parent->hasEvalError() )
8241 {
8242 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_file"_L1 ) );
8243 return QVariant();
8244 }
8245 return QFileInfo( file ).isFile();
8246}
8247
8248static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8249{
8250 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8251 if ( parent->hasEvalError() )
8252 {
8253 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8254 return QVariant();
8255 }
8256 return QFileInfo( file ).isDir();
8257}
8258
8259static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8260{
8261 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8262 if ( parent->hasEvalError() )
8263 {
8264 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8265 return QVariant();
8266 }
8267 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8268}
8269
8270static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8271{
8272 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8273 if ( parent->hasEvalError() )
8274 {
8275 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8276 return QVariant();
8277 }
8278 return QFileInfo( file ).size();
8279}
8280
8281static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8282{
8283 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8284}
8285
8286static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8287{
8288 QVariant hash;
8289 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8290 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8291
8292 if ( method == "md4"_L1 )
8293 {
8294 hash = fcnHash( str, QCryptographicHash::Md4 );
8295 }
8296 else if ( method == "md5"_L1 )
8297 {
8298 hash = fcnHash( str, QCryptographicHash::Md5 );
8299 }
8300 else if ( method == "sha1"_L1 )
8301 {
8302 hash = fcnHash( str, QCryptographicHash::Sha1 );
8303 }
8304 else if ( method == "sha224"_L1 )
8305 {
8306 hash = fcnHash( str, QCryptographicHash::Sha224 );
8307 }
8308 else if ( method == "sha256"_L1 )
8309 {
8310 hash = fcnHash( str, QCryptographicHash::Sha256 );
8311 }
8312 else if ( method == "sha384"_L1 )
8313 {
8314 hash = fcnHash( str, QCryptographicHash::Sha384 );
8315 }
8316 else if ( method == "sha512"_L1 )
8317 {
8318 hash = fcnHash( str, QCryptographicHash::Sha512 );
8319 }
8320 else if ( method == "sha3_224"_L1 )
8321 {
8322 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8323 }
8324 else if ( method == "sha3_256"_L1 )
8325 {
8326 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8327 }
8328 else if ( method == "sha3_384"_L1 )
8329 {
8330 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8331 }
8332 else if ( method == "sha3_512"_L1 )
8333 {
8334 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8335 }
8336 else if ( method == "keccak_224"_L1 )
8337 {
8338 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8339 }
8340 else if ( method == "keccak_256"_L1 )
8341 {
8342 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8343 }
8344 else if ( method == "keccak_384"_L1 )
8345 {
8346 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8347 }
8348 else if ( method == "keccak_512"_L1 )
8349 {
8350 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8351 }
8352 else
8353 {
8354 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8355 }
8356 return hash;
8357}
8358
8359static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8360{
8361 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8362}
8363
8364static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8365{
8366 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8367}
8368
8369static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8370{
8371 const QByteArray input = values.at( 0 ).toByteArray();
8372 return QVariant( QString( input.toBase64() ) );
8373}
8374
8375static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8376{
8377 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8378 QUrlQuery query;
8379 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8380 {
8381 query.addQueryItem( it.key(), it.value().toString() );
8382 }
8383 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8385
8386static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8387{
8388 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8389 const QByteArray base64 = value.toLocal8Bit();
8390 const QByteArray decoded = QByteArray::fromBase64( base64 );
8391 return QVariant( decoded );
8392}
8393
8395typedef std::function<bool( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend )> RelationFunction;
8396
8397static QVariant executeGeomOverlay(
8398 const QVariantList &values,
8399 const QgsExpressionContext *context,
8400 QgsExpression *parent,
8401 const RelationFunction &relationFunction,
8402 bool invert = false,
8403 double bboxGrow = 0,
8404 bool isNearestFunc = false,
8405 bool isIntersectsFunc = false
8406)
8407{
8408 if ( !context )
8409 {
8410 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8411 return QVariant();
8412 }
8413
8414 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8415 // TODO this function is NOT thread safe
8417 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8419
8420 QgsFeatureRequest request;
8421 request.setTimeout( 10000 );
8422 request.setRequestMayBeNested( true );
8423 request.setFeedback( context->feedback() );
8424
8425 // First parameter is the overlay layer
8426 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8428
8429 const bool layerCanBeCached = node->isStatic( parent, context );
8430 QVariant targetLayerValue = node->eval( parent, context );
8432
8433 // Second parameter is the expression to evaluate (or null for testonly)
8434 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8436 QString subExpString = node->dump();
8437
8438 bool testOnly = ( subExpString == "NULL" );
8439 // TODO this function is NOT thread safe
8441 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8443 if ( !targetLayer ) // No layer, no joy
8444 {
8445 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8446 return QVariant();
8447 }
8448
8449 // Third parameter is the filtering expression
8450 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8452 QString filterString = node->dump();
8453 if ( filterString != "NULL" )
8454 {
8455 request.setFilterExpression( filterString ); //filter cached features
8456 }
8457
8458 // Fourth parameter is the limit
8459 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8461 QVariant limitValue = node->eval( parent, context );
8463 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8464
8465 double max_distance = 0;
8466 bool cacheEnabled = false;
8467
8468 double minOverlap { -1 };
8469 double minInscribedCircleRadius { -1 };
8470 bool returnDetails = false; //#spellok
8471 bool sortByMeasure = false;
8472 bool sortAscending = false;
8473 bool requireMeasures = false;
8474 bool overlapOrRadiusFilter = false;
8475
8477
8478 if ( isNearestFunc ) //maxdistance param handling
8479 {
8480 // Fifth parameter (for nearest only) is the max distance
8481 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8483 QVariant distanceValue = node->eval( parent, context );
8485 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8486
8487 // Sixth (for nearest only) parameter is the cache toggle
8488 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
8490 QVariant cacheValue = node->eval( parent, context );
8492 cacheEnabled = cacheValue.toBool();
8493 }
8494 else
8495 {
8496 // Fifth parameter is the cache toggle
8497 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8499 QVariant cacheValue = node->eval( parent, context );
8501 cacheEnabled = cacheValue.toBool();
8502
8503 // Sixth parameter is the min overlap (area or length)
8504 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8506 const QVariant minOverlapValue = node->eval( parent, context );
8508 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8509
8510 // Seventh parameter is the min inscribed circle radius
8511 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8513 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8515 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8516
8517 // Eighth parameter is the return_details
8518 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8519 // Return measures is only effective when an expression is set
8520 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8521
8522 // Ninth parameter is the sort_by_intersection_size flag
8523 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8524 // Sort by measures is only effective when an expression is set
8525 const QString sorting { node->eval( parent, context ).toString().toLower() };
8526 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8527 sortAscending = sorting.startsWith( "asc" );
8528 requireMeasures = sortByMeasure || returnDetails; //#spellok
8529 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8530
8531 // Tenth parameter is the geometry backend
8532 node = QgsExpressionUtils::getNode( values.at( 9 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8534 const QString backendStr = node->eval( parent, context ).toString().toUpper();
8536
8537 bool ok;
8538 backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
8539 if ( !ok )
8540 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
8541 }
8542
8543 FEAT_FROM_CONTEXT( context, feat )
8544 const QgsGeometry geometry = feat.geometry();
8545
8546 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8547 {
8548 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8549 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8550 }
8551
8552 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8553
8554 QgsRectangle intDomain = geometry.boundingBox();
8555 if ( bboxGrow != 0 )
8556 {
8557 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8558 }
8559
8560 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8561
8562 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8563 // Otherwise, it can be toggled by the user
8564 QgsSpatialIndex spatialIndex;
8565 QgsVectorLayer *cachedTarget;
8566 QList<QgsFeature> features;
8567 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8568 {
8569 // If the cache (local spatial index) is enabled, we materialize the whole
8570 // layer, then do the request on that layer instead.
8571 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8572 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8573
8574 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8575 {
8576 cachedTarget = targetLayer->materialize( request );
8577 if ( layerCanBeCached )
8578 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8579 }
8580 else
8581 {
8582 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8583 }
8584
8585 if ( !context->hasCachedValue( cacheIndex ) )
8586 {
8587 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8588 if ( layerCanBeCached )
8589 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8590 }
8591 else
8592 {
8593 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8594 }
8595
8596 QList<QgsFeatureId> fidsList;
8597 if ( isNearestFunc )
8598 {
8599 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8600 }
8601 else
8602 {
8603 fidsList = spatialIndex.intersects( intDomain );
8604 }
8605
8606 QListIterator<QgsFeatureId> i( fidsList );
8607 while ( i.hasNext() )
8608 {
8609 QgsFeatureId fId2 = i.next();
8610 if ( sameLayers && feat.id() == fId2 )
8611 continue;
8612 features.append( cachedTarget->getFeature( fId2 ) );
8613 }
8614 }
8615 else
8616 {
8617 // If the cache (local spatial index) is not enabled, we directly
8618 // get the features from the target layer
8619 request.setFilterRect( intDomain );
8620 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8621 QgsFeature feat2;
8622 while ( fit.nextFeature( feat2 ) )
8623 {
8624 if ( sameLayers && feat.id() == feat2.id() )
8625 continue;
8626 features.append( feat2 );
8627 }
8628 }
8629
8630 QgsExpression subExpression;
8631 QgsExpressionContext subContext;
8632 if ( !testOnly )
8633 {
8634 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8635 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8636
8637 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8638 {
8639 subExpression = QgsExpression( subExpString );
8641 subExpression.prepare( &subContext );
8642 }
8643 else
8644 {
8645 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8646 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8647 }
8648 }
8649
8650 // //////////////////////////////////////////////////////////////////
8651 // Helper functions for geometry tests
8652
8653 // Test function for linestring geometries, returns TRUE if test passes
8654 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double &overlapValue ) -> bool {
8655 bool testResult { false };
8656 // For return measures:
8657 QVector<double> overlapValues;
8658 const QgsGeometry merged { intersection.mergeLines() };
8659 for ( auto it = merged.const_parts_begin(); !testResult && it != merged.const_parts_end(); ++it )
8660 {
8662 // Check min overlap for intersection (if set)
8663 if ( minOverlap != -1 || requireMeasures )
8664 {
8665 overlapValue = geom->length();
8666 overlapValues.append( overlapValue );
8667 if ( minOverlap != -1 )
8668 {
8669 if ( overlapValue >= minOverlap )
8670 {
8671 testResult = true;
8672 }
8673 else
8674 {
8675 continue;
8676 }
8677 }
8678 }
8679 }
8680
8681 if ( !overlapValues.isEmpty() )
8682 {
8683 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8684 }
8685
8686 return testResult;
8687 };
8688
8689 // Test function for polygon geometries, returns TRUE if test passes
8690 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double &radiusValue, double &overlapValue ) -> bool {
8691 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8692 bool testResult { false };
8693 // For return measures:
8694 QVector<double> overlapValues;
8695 QVector<double> radiusValues;
8696 for ( auto it = intersection.const_parts_begin(); ( !testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8697 {
8699 // Check min overlap for intersection (if set)
8700 if ( minOverlap != -1 || requireMeasures )
8701 {
8702 overlapValue = geom->area();
8703 overlapValues.append( geom->area() );
8704 if ( minOverlap != -1 )
8705 {
8706 if ( overlapValue >= minOverlap )
8707 {
8708 testResult = true;
8709 }
8710 else
8711 {
8712 continue;
8713 }
8714 }
8715 }
8716
8717 // Check min inscribed circle radius for intersection (if set)
8718 if ( minInscribedCircleRadius != -1 || requireMeasures )
8719 {
8720 const QgsRectangle bbox = geom->boundingBox();
8721 const double width = bbox.width();
8722 const double height = bbox.height();
8723 const double size = width > height ? width : height;
8724 const double tolerance = size / 100.0;
8725 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8726 testResult = radiusValue >= minInscribedCircleRadius;
8727 radiusValues.append( radiusValues );
8728 }
8729 } // end for parts
8730
8731 // Get the max values
8732 if ( !radiusValues.isEmpty() )
8733 {
8734 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8735 }
8736
8737 if ( !overlapValues.isEmpty() )
8738 {
8739 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8740 }
8741
8742 return testResult;
8743 };
8744
8745
8746 bool found = false;
8747 int foundCount = 0;
8748 QVariantList results;
8749
8750 QListIterator<QgsFeature> i( features );
8751 try
8752 {
8753 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8754 {
8755 QgsFeature feat2 = i.next();
8756
8757
8758 if ( relationFunction( geometry, feat2.geometry(), values, backend ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8759 {
8760 double overlapValue = -1;
8761 double radiusValue = -1;
8762
8763 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8764 {
8765 QgsGeometry intersection { geometry.intersection( feat2.geometry(), QgsGeometryParameters() ) };
8766
8767 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8768 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8769 {
8770 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8771 intersection = QgsGeometry();
8772 QgsMultiPolygonXY poly;
8773 QgsMultiPolylineXY line;
8774 QgsMultiPointXY point;
8775 for ( const auto &geom : std::as_const( geometries ) )
8776 {
8777 switch ( geom.type() )
8778 {
8780 {
8781 poly.append( geom.asPolygon() );
8782 break;
8783 }
8785 {
8786 line.append( geom.asPolyline() );
8787 break;
8788 }
8790 {
8791 point.append( geom.asPoint() );
8792 break;
8793 }
8796 {
8797 break;
8798 }
8799 }
8800 }
8801
8802 switch ( geometry.type() )
8803 {
8805 {
8806 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8807 break;
8808 }
8810 {
8811 intersection = QgsGeometry::fromMultiPolylineXY( line );
8812 break;
8813 }
8815 {
8816 intersection = QgsGeometry::fromMultiPointXY( point );
8817 break;
8818 }
8821 {
8822 break;
8823 }
8824 }
8825 }
8826
8827 // Depending on the intersection geometry type and on the geometry type of
8828 // the tested geometry we can run different tests and collect different measures
8829 // that can be used for sorting (if required).
8830 switch ( intersection.type() )
8831 {
8833 {
8834 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8835 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8836
8837 if ( !testResult && overlapOrRadiusFilter )
8838 {
8839 continue;
8840 }
8841
8842 break;
8843 }
8844
8846 {
8847 // If the intersection is a linestring and a minimum circle is required
8848 // we can discard this result immediately.
8849 if ( minInscribedCircleRadius != -1 )
8850 {
8851 continue;
8852 }
8853
8854 // Otherwise a test for the overlap value is performed.
8855 const bool testResult { testLinestring( intersection, overlapValue ) };
8856
8857 if ( !testResult && overlapOrRadiusFilter )
8858 {
8859 continue;
8860 }
8861
8862 break;
8863 }
8864
8866 {
8867 // If the intersection is a point and a minimum circle is required
8868 // we can discard this result immediately.
8869 if ( minInscribedCircleRadius != -1 )
8870 {
8871 continue;
8872 }
8873
8874 bool testResult { false };
8875 if ( minOverlap != -1 || requireMeasures )
8876 {
8877 // Initially set this to 0 because it's a point intersection...
8878 overlapValue = 0;
8879 // ... but if the target geometry is not a point and the source
8880 // geometry is a point, we must record the length or the area
8881 // of the intersected geometry and use that as a measure for
8882 // sorting or reporting.
8883 if ( geometry.type() == Qgis::GeometryType::Point )
8884 {
8885 switch ( feat2.geometry().type() )
8886 {
8890 {
8891 break;
8892 }
8894 {
8895 testResult = testLinestring( feat2.geometry(), overlapValue );
8896 break;
8897 }
8899 {
8900 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8901 break;
8902 }
8903 }
8904 }
8905
8906 if ( !testResult && overlapOrRadiusFilter )
8907 {
8908 continue;
8909 }
8910 }
8911 break;
8912 }
8913
8916 {
8917 continue;
8918 }
8919 }
8920 }
8921
8922 found = true;
8923 foundCount++;
8924
8925 // We just want a single boolean result if there is any intersect: finish and return true
8926 if ( testOnly )
8927 break;
8928
8929 if ( !invert )
8930 {
8931 // We want a list of attributes / geometries / other expression values, evaluate now
8932 subContext.setFeature( feat2 );
8933 const QVariant expResult = subExpression.evaluate( &subContext );
8934
8935 if ( requireMeasures )
8936 {
8937 QVariantMap resultRecord;
8938 resultRecord.insert( u"id"_s, feat2.id() );
8939 resultRecord.insert( u"result"_s, expResult );
8940 // Overlap is always added because return measures was set
8941 resultRecord.insert( u"overlap"_s, overlapValue );
8942 // Radius is only added when is different than -1 (because for linestrings is not set)
8943 if ( radiusValue != -1 )
8944 {
8945 resultRecord.insert( u"radius"_s, radiusValue );
8946 }
8947 results.append( resultRecord );
8948 }
8949 else
8950 {
8951 results.append( expResult );
8952 }
8953 }
8954 else
8955 {
8956 // If not, results is a list of found ids, which we'll inverse and evaluate below
8957 results.append( feat2.id() );
8958 }
8959 }
8960 }
8961 }
8962 catch ( QgsException &e )
8963 {
8964 parent->setEvalErrorString( e.what() );
8965 return false;
8966 }
8967
8968 if ( testOnly )
8969 {
8970 if ( invert )
8971 found = !found; //for disjoint condition
8972 return found;
8973 }
8974
8975 if ( !invert )
8976 {
8977 if ( requireMeasures )
8978 {
8979 if ( sortByMeasure )
8980 {
8981 std::sort( results.begin(), results.end(), [sortAscending]( const QVariant &recordA, const QVariant &recordB ) -> bool {
8982 return sortAscending ? recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
8983 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
8984 } );
8985 }
8986 // Resize
8987 if ( limit > 0 && results.size() > limit )
8988 {
8989 results.erase( results.begin() + limit );
8990 }
8991
8992 if ( !returnDetails ) //#spellok
8993 {
8994 QVariantList expResults;
8995 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8996 {
8997 expResults.append( it->toMap().value( u"result"_s ) );
8998 }
8999 return expResults;
9000 }
9001 }
9002
9003 return results;
9004 }
9005
9006 // for disjoint condition returns the results for cached layers not intersected feats
9007 QVariantList disjoint_results;
9008 QgsFeature feat2;
9009 QgsFeatureRequest request2;
9010 request2.setLimit( limit );
9011 if ( context )
9012 request2.setFeedback( context->feedback() );
9013 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
9014 while ( fi.nextFeature( feat2 ) )
9015 {
9016 if ( !results.contains( feat2.id() ) )
9017 {
9018 subContext.setFeature( feat2 );
9019 disjoint_results.append( subExpression.evaluate( &subContext ) );
9020 }
9021 }
9022 return disjoint_results;
9023}
9024
9025// Intersect functions:
9026
9027static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9028{
9029 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9030 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, false, true );
9031}
9032
9033static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9034{
9035 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.contains( other ); };
9036 return executeGeomOverlay( values, context, parent, geomFunction );
9037}
9038
9039static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9040{
9041 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.crosses( other ); };
9042 return executeGeomOverlay( values, context, parent, geomFunction );
9043}
9044
9045static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9046{
9047 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9048 return geometry.isExactlyEqual( other, Qgis::GeometryBackend::QGIS );
9049 };
9050 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9051}
9052
9053static QVariant fcnGeomOverlayEqualsExact( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9054{
9055 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9056 return geometry.isExactlyEqual( other, backend );
9057 };
9058 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9059}
9060
9061static QVariant fcnGeomOverlayEqualsTopological( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9062{
9063 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9064 return geometry.isTopologicallyEqual( other, backend );
9065 };
9066 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9067}
9068
9069static QVariant fcnGeomOverlayEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9070{
9071 // This parameter is the epsilon tolerance
9072 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 10 ), parent );
9074 QVariant epsilonValue = node->eval( parent, context );
9076 double epsilon = QgsExpressionUtils::getDoubleValue( epsilonValue, parent );
9077
9078 RelationFunction geomFunction = [epsilon]( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9079 return geometry.isFuzzyEqual( other, epsilon, backend );
9080 };
9081 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9082}
9083
9084static QVariant fcnGeomOverlayTouches( 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.touches( other ); };
9087 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9088}
9089
9090static QVariant fcnGeomOverlayWithin( 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 { return geometry.within( other ); };
9093 return executeGeomOverlay( values, context, parent, geomFunction );
9094}
9095
9096static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9097{
9098 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9099 return executeGeomOverlay( values, context, parent, geomFunction, true, 0, false, true );
9100}
9101
9102static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9103{
9104 RelationFunction geomFunction = []( const QgsGeometry &, const QgsGeometry &, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9105 return true; // does nothing on purpose
9106 };
9107 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, true );
9108}
9109
9110const QList<QgsExpressionFunction *> &QgsExpression::Functions()
9111{
9112 // The construction of the list isn't thread-safe, and without the mutex,
9113 // crashes in the WFS provider may occur, since it can parse expressions
9114 // in parallel.
9115 // The mutex needs to be recursive.
9116 QMutexLocker locker( &sFunctionsMutex );
9117
9118 QList<QgsExpressionFunction *> &functions = *sFunctions();
9119
9120 if ( functions.isEmpty() )
9121 {
9123 << QgsExpressionFunction::Parameter( u"expression"_s )
9124 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
9125 << QgsExpressionFunction::Parameter( u"filter"_s, true );
9126
9127 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
9128 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true ) << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9129
9130 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
9131 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9132
9133 functions
9134 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
9135 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
9136 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
9137 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnAzimuth, u"GeometryGroup"_s )
9138 << new QgsStaticExpressionFunction(
9139 u"bearing"_s,
9141 << QgsExpressionFunction::Parameter( u"point1"_s )
9142 << QgsExpressionFunction::Parameter( u"point2"_s )
9143 << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() )
9144 << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ),
9145 fcnBearing,
9146 u"GeometryGroup"_s
9147 )
9148 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnInclination, u"GeometryGroup"_s )
9149 << new QgsStaticExpressionFunction(
9150 u"project"_s,
9152 << QgsExpressionFunction::Parameter( u"point"_s )
9153 << QgsExpressionFunction::Parameter( u"distance"_s )
9154 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9155 << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ),
9156 fcnProject,
9157 u"GeometryGroup"_s
9158 )
9159 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
9160 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
9161 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
9162 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
9163 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
9164 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
9165 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
9166 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
9167 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
9168 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
9169 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
9170 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
9171 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
9172
9173 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction(
9174 u"rand"_s,
9175 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9176 fcnRnd,
9177 u"Math"_s
9178 );
9179 randFunc->setIsStatic( false );
9180 functions << randFunc;
9181
9182 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction(
9183 u"randf"_s,
9185 << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 )
9186 << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 )
9187 << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9188 fcnRndF,
9189 u"Math"_s
9190 );
9191 randfFunc->setIsStatic( false );
9192 functions << randfFunc;
9193
9194 functions
9195 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9196 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9197 << new QgsStaticExpressionFunction( u"clamp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"max"_s ), fcnClamp, u"Math"_s )
9198 << new QgsStaticExpressionFunction(
9199 u"scale_linear"_s,
9201 << QgsExpressionFunction::Parameter( u"value"_s )
9202 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9203 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9204 << QgsExpressionFunction::Parameter( u"range_min"_s )
9205 << QgsExpressionFunction::Parameter( u"range_max"_s ),
9206 fcnLinearScale,
9207 u"Math"_s
9208 )
9209 << new QgsStaticExpressionFunction(
9210 u"scale_polynomial"_s,
9212 << QgsExpressionFunction::Parameter( u"value"_s )
9213 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9214 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9215 << QgsExpressionFunction::Parameter( u"range_min"_s )
9216 << QgsExpressionFunction::Parameter( u"range_max"_s )
9217 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9218 fcnPolynomialScale,
9219 u"Math"_s,
9220 QString(),
9221 false,
9222 QSet<QString>(),
9223 false,
9224 QStringList() << u"scale_exp"_s
9225 )
9226 << new QgsStaticExpressionFunction(
9227 u"scale_exponential"_s,
9229 << QgsExpressionFunction::Parameter( u"value"_s )
9230 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9231 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9232 << QgsExpressionFunction::Parameter( u"range_min"_s )
9233 << QgsExpressionFunction::Parameter( u"range_max"_s )
9234 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9235 fcnExponentialScale,
9236 u"Math"_s
9237 )
9238 << new QgsStaticExpressionFunction(
9239 u"scale_cubic_bezier"_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 << QgsExpressionFunction::Parameter( u"x1"_s )
9247 << QgsExpressionFunction::Parameter( u"y1"_s )
9248 << QgsExpressionFunction::Parameter( u"x2"_s )
9249 << QgsExpressionFunction::Parameter( u"y2"_s ),
9250 fcnCubicBezierScale,
9251 u"Math"_s
9252 )
9253 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
9254 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
9255 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
9256 << new QgsStaticExpressionFunction( u"to_bool"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBool, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"tobool"_s, /* handlesNull = */ true )
9257 << new QgsStaticExpressionFunction( u"to_int"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToInt, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"toint"_s )
9258 << new QgsStaticExpressionFunction( u"to_real"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToReal, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"toreal"_s )
9259 << new QgsStaticExpressionFunction(
9260 u"to_string"_s,
9261 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9262 fcnToString,
9263 QStringList() << u"Conversions"_s << u"String"_s,
9264 QString(),
9265 false,
9266 QSet<QString>(),
9267 false,
9268 QStringList() << u"tostring"_s
9269 )
9270 << new QgsStaticExpressionFunction(
9271 u"to_datetime"_s,
9273 << QgsExpressionFunction::Parameter( u"value"_s )
9274 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9275 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9276 fcnToDateTime,
9277 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9278 QString(),
9279 false,
9280 QSet<QString>(),
9281 false,
9282 QStringList() << u"todatetime"_s
9283 )
9284 << new QgsStaticExpressionFunction(
9285 u"to_date"_s,
9287 << QgsExpressionFunction::Parameter( u"value"_s )
9288 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9289 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9290 fcnToDate,
9291 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9292 QString(),
9293 false,
9294 QSet<QString>(),
9295 false,
9296 QStringList() << u"todate"_s
9297 )
9298 << new QgsStaticExpressionFunction(
9299 u"to_time"_s,
9301 << QgsExpressionFunction::Parameter( u"value"_s )
9302 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9303 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9304 fcnToTime,
9305 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9306 QString(),
9307 false,
9308 QSet<QString>(),
9309 false,
9310 QStringList() << u"totime"_s
9311 )
9312 << new QgsStaticExpressionFunction(
9313 u"to_interval"_s,
9314 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9315 fcnToInterval,
9316 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9317 QString(),
9318 false,
9319 QSet<QString>(),
9320 false,
9321 QStringList() << u"tointerval"_s
9322 )
9323 << new QgsStaticExpressionFunction(
9324 u"to_dm"_s,
9326 << QgsExpressionFunction::Parameter( u"value"_s )
9327 << QgsExpressionFunction::Parameter( u"axis"_s )
9328 << QgsExpressionFunction::Parameter( u"precision"_s )
9329 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9330 fcnToDegreeMinute,
9331 u"Conversions"_s,
9332 QString(),
9333 false,
9334 QSet<QString>(),
9335 false,
9336 QStringList() << u"todm"_s
9337 )
9338 << new QgsStaticExpressionFunction(
9339 u"to_dms"_s,
9341 << QgsExpressionFunction::Parameter( u"value"_s )
9342 << QgsExpressionFunction::Parameter( u"axis"_s )
9343 << QgsExpressionFunction::Parameter( u"precision"_s )
9344 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9345 fcnToDegreeMinuteSecond,
9346 u"Conversions"_s,
9347 QString(),
9348 false,
9349 QSet<QString>(),
9350 false,
9351 QStringList() << u"todms"_s
9352 )
9353 << new QgsStaticExpressionFunction( u"to_decimal"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToDecimal, u"Conversions"_s, QString(), false, QSet<QString>(), false, QStringList() << u"todecimal"_s )
9354 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
9355 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
9356 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
9357 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9358 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
9359 << new QgsStaticExpressionFunction(
9360 u"if"_s,
9362 << QgsExpressionFunction::Parameter( u"condition"_s )
9363 << QgsExpressionFunction::Parameter( u"result_when_true"_s )
9364 << QgsExpressionFunction::Parameter( u"result_when_false"_s ),
9365 fcnIf,
9366 u"Conditionals"_s,
9367 QString(),
9368 false,
9369 QSet<QString>(),
9370 true
9371 )
9372 << new QgsStaticExpressionFunction(
9373 u"try"_s,
9374 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ),
9375 fcnTry,
9376 u"Conditionals"_s,
9377 QString(),
9378 false,
9379 QSet<QString>(),
9380 true
9381 )
9382
9383 << new QgsStaticExpressionFunction(
9384 u"aggregate"_s,
9386 << QgsExpressionFunction::Parameter( u"layer"_s )
9387 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9388 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9389 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9390 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9391 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9392 fcnAggregate,
9393 u"Aggregates"_s,
9394 QString(),
9395 []( const QgsExpressionNodeFunction *node ) {
9396 // usesGeometry callback: return true if @parent variable is referenced
9397
9398 if ( !node )
9399 return true;
9400
9401 if ( !node->args() )
9402 return false;
9403
9404 QSet<QString> referencedVars;
9405 if ( node->args()->count() > 2 )
9406 {
9407 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9408 referencedVars = subExpressionNode->referencedVariables();
9409 }
9410
9411 if ( node->args()->count() > 3 )
9412 {
9413 QgsExpressionNode *filterNode = node->args()->at( 3 );
9414 referencedVars.unite( filterNode->referencedVariables() );
9415 }
9416 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
9417 },
9418 []( const QgsExpressionNodeFunction *node ) {
9419 // referencedColumns callback: return AllAttributes if @parent variable is referenced
9420
9421 if ( !node )
9422 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9423
9424 if ( !node->args() )
9425 return QSet<QString>();
9426
9427 QSet<QString> referencedCols;
9428 QSet<QString> referencedVars;
9429
9430 if ( node->args()->count() > 2 )
9431 {
9432 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9433 referencedVars = subExpressionNode->referencedVariables();
9434 referencedCols = subExpressionNode->referencedColumns();
9435 }
9436 if ( node->args()->count() > 3 )
9437 {
9438 QgsExpressionNode *filterNode = node->args()->at( 3 );
9439 referencedVars = filterNode->referencedVariables();
9440 referencedCols.unite( filterNode->referencedColumns() );
9441 }
9442
9443 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
9444 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9445 else
9446 return referencedCols;
9447 },
9448 true
9449 )
9450
9451 << new QgsStaticExpressionFunction(
9452 u"relation_aggregate"_s,
9454 << QgsExpressionFunction::Parameter( u"relation"_s )
9455 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9456 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9457 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9458 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9459 fcnAggregateRelation,
9460 u"Aggregates"_s,
9461 QString(),
9462 false,
9463 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9464 true
9465 )
9466
9467 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9468 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9469 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9470 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9471 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9472 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9473 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9474 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9475 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9476 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9477 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9478 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9479 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9480 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9481 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9482 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9483 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9484 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9485 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9486 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9487 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9488
9489 << new QgsStaticExpressionFunction( u"regexp_match"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpMatch, QStringList() << u"Conditionals"_s << u"String"_s )
9490 << new QgsStaticExpressionFunction(
9491 u"regexp_matches"_s,
9493 << QgsExpressionFunction::Parameter( u"string"_s )
9494 << QgsExpressionFunction::Parameter( u"regex"_s )
9495 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
9496 fcnRegexpMatches,
9497 u"Arrays"_s
9498 )
9499
9500 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9501 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s ) << QgsExpressionFunction::Parameter( u"datetime2"_s ), fcnAge, u"Date and Time"_s )
9502 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9503 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9504 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9505 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9506 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9507 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9508 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9509 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9510 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9511 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9512 << new QgsStaticExpressionFunction( u"make_date"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"year"_s ) << QgsExpressionFunction::Parameter( u"month"_s ) << QgsExpressionFunction::Parameter( u"day"_s ), fcnMakeDate, u"Date and Time"_s )
9513 << new QgsStaticExpressionFunction(
9514 u"make_time"_s,
9515 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s ) << QgsExpressionFunction::Parameter( u"minute"_s ) << QgsExpressionFunction::Parameter( u"second"_s ),
9516 fcnMakeTime,
9517 u"Date and Time"_s
9518 )
9519 << new QgsStaticExpressionFunction(
9520 u"make_datetime"_s,
9522 << QgsExpressionFunction::Parameter( u"year"_s )
9523 << QgsExpressionFunction::Parameter( u"month"_s )
9524 << QgsExpressionFunction::Parameter( u"day"_s )
9525 << QgsExpressionFunction::Parameter( u"hour"_s )
9526 << QgsExpressionFunction::Parameter( u"minute"_s )
9527 << QgsExpressionFunction::Parameter( u"second"_s ),
9528 fcnMakeDateTime,
9529 u"Date and Time"_s
9530 )
9531 << new QgsStaticExpressionFunction(
9532 u"make_interval"_s,
9534 << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9535 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9536 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9537 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9538 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9539 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9540 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9541 fcnMakeInterval,
9542 u"Date and Time"_s
9543 )
9544 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9545 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9546 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9547 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9548 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9549 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9550 << new QgsStaticExpressionFunction(
9551 u"substr_count"_s,
9553 << QgsExpressionFunction::Parameter( u"string"_s )
9554 << QgsExpressionFunction::Parameter( u"substring"_s )
9555 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9556 fcnSubstrCount,
9557 u"String"_s
9558 )
9559 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9560 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9561 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9562 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9563 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9564 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9565 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9566 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9567 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9568 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9569 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9570 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9571 << new QgsStaticExpressionFunction(
9572 u"wordwrap"_s,
9574 << QgsExpressionFunction::Parameter( u"text"_s )
9575 << QgsExpressionFunction::Parameter( u"length"_s )
9576 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ),
9577 fcnWordwrap,
9578 u"String"_s
9579 )
9580 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9581 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9582 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s ) }, fcnRepeat, u"String"_s )
9583 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9584 << new QgsStaticExpressionFunction(
9585 u"regexp_replace"_s,
9586 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"replacement"_s ),
9587 fcnRegexpReplace,
9588 u"String"_s
9589 )
9590 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9591 << new QgsStaticExpressionFunction(
9592 u"substr"_s,
9593 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ),
9594 fcnSubstr,
9595 u"String"_s,
9596 QString(),
9597 false,
9598 QSet< QString >(),
9599 false,
9600 QStringList(),
9601 true
9602 )
9603 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9604 << new QgsStaticExpressionFunction( u"concat_ws"_s, -1, fcnConcatWs, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9605 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9606 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9607 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9608 << new QgsStaticExpressionFunction( u"rpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnRPad, u"String"_s )
9609 << new QgsStaticExpressionFunction( u"lpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnLPad, u"String"_s )
9610 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9611 << new QgsStaticExpressionFunction(
9612 u"format_number"_s,
9614 << QgsExpressionFunction::Parameter( u"number"_s )
9615 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9616 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9617 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9618 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ),
9619 fcnFormatNumber,
9620 u"String"_s
9621 )
9622 << new QgsStaticExpressionFunction(
9623 u"format_date"_s,
9625 << QgsExpressionFunction::Parameter( u"datetime"_s )
9626 << QgsExpressionFunction::Parameter( u"format"_s )
9627 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9628 fcnFormatDate,
9629 QStringList() << u"String"_s << u"Date and Time"_s
9630 )
9631 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9632 << new QgsStaticExpressionFunction(
9633 u"color_mix_rgb"_s,
9634 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9635 fcnColorMixRgb,
9636 u"Color"_s
9637 )
9638 << new QgsStaticExpressionFunction(
9639 u"color_mix"_s,
9640 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9641 fcnColorMix,
9642 u"Color"_s
9643 )
9644 << new 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 )
9645 << new QgsStaticExpressionFunction(
9646 u"color_rgbf"_s,
9648 << QgsExpressionFunction::Parameter( u"red"_s )
9649 << QgsExpressionFunction::Parameter( u"green"_s )
9650 << QgsExpressionFunction::Parameter( u"blue"_s )
9651 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9652 fcnColorRgbF,
9653 u"Color"_s
9654 )
9655 << new QgsStaticExpressionFunction(
9656 u"color_rgba"_s,
9658 << QgsExpressionFunction::Parameter( u"red"_s )
9659 << QgsExpressionFunction::Parameter( u"green"_s )
9660 << QgsExpressionFunction::Parameter( u"blue"_s )
9661 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9662 fncColorRgba,
9663 u"Color"_s
9664 )
9665 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColor, u"Color"_s )
9666 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColorObject, u"Color"_s )
9667 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ), fcnCreateRamp, u"Color"_s )
9668 << new QgsStaticExpressionFunction(
9669 u"color_hsl"_s,
9670 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"lightness"_s ),
9671 fcnColorHsl,
9672 u"Color"_s
9673 )
9674 << new QgsStaticExpressionFunction(
9675 u"color_hsla"_s,
9677 << QgsExpressionFunction::Parameter( u"hue"_s )
9678 << QgsExpressionFunction::Parameter( u"saturation"_s )
9679 << QgsExpressionFunction::Parameter( u"lightness"_s )
9680 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9681 fncColorHsla,
9682 u"Color"_s
9683 )
9684 << new QgsStaticExpressionFunction(
9685 u"color_hslf"_s,
9687 << QgsExpressionFunction::Parameter( u"hue"_s )
9688 << QgsExpressionFunction::Parameter( u"saturation"_s )
9689 << QgsExpressionFunction::Parameter( u"lightness"_s )
9690 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9691 fcnColorHslF,
9692 u"Color"_s
9693 )
9694 << new QgsStaticExpressionFunction(
9695 u"color_hsv"_s,
9696 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9697 fcnColorHsv,
9698 u"Color"_s
9699 )
9700 << new QgsStaticExpressionFunction(
9701 u"color_hsva"_s,
9703 << QgsExpressionFunction::Parameter( u"hue"_s )
9704 << QgsExpressionFunction::Parameter( u"saturation"_s )
9705 << QgsExpressionFunction::Parameter( u"value"_s )
9706 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9707 fncColorHsva,
9708 u"Color"_s
9709 )
9710 << new QgsStaticExpressionFunction(
9711 u"color_hsvf"_s,
9713 << QgsExpressionFunction::Parameter( u"hue"_s )
9714 << QgsExpressionFunction::Parameter( u"saturation"_s )
9715 << QgsExpressionFunction::Parameter( u"value"_s )
9716 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9717 fcnColorHsvF,
9718 u"Color"_s
9719 )
9720 << new QgsStaticExpressionFunction(
9721 u"color_cmyk"_s,
9723 << QgsExpressionFunction::Parameter( u"cyan"_s )
9724 << QgsExpressionFunction::Parameter( u"magenta"_s )
9725 << QgsExpressionFunction::Parameter( u"yellow"_s )
9726 << QgsExpressionFunction::Parameter( u"black"_s ),
9727 fcnColorCmyk,
9728 u"Color"_s
9729 )
9730 << new QgsStaticExpressionFunction(
9731 u"color_cmyka"_s,
9733 << QgsExpressionFunction::Parameter( u"cyan"_s )
9734 << QgsExpressionFunction::Parameter( u"magenta"_s )
9735 << QgsExpressionFunction::Parameter( u"yellow"_s )
9736 << QgsExpressionFunction::Parameter( u"black"_s )
9737 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9738 fncColorCmyka,
9739 u"Color"_s
9740 )
9741 << new QgsStaticExpressionFunction(
9742 u"color_cmykf"_s,
9744 << QgsExpressionFunction::Parameter( u"cyan"_s )
9745 << QgsExpressionFunction::Parameter( u"magenta"_s )
9746 << QgsExpressionFunction::Parameter( u"yellow"_s )
9747 << QgsExpressionFunction::Parameter( u"black"_s )
9748 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9749 fcnColorCmykF,
9750 u"Color"_s
9751 )
9752 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ), fncColorPart, u"Color"_s )
9753 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncDarker, u"Color"_s )
9754 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncLighter, u"Color"_s )
9755 << new QgsStaticExpressionFunction(
9756 u"set_color_part"_s,
9757 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9758 fncSetColorPart,
9759 u"Color"_s
9760 )
9761
9762 // file info
9763 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnBaseFileName, u"Files and Paths"_s )
9764 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSuffix, u"Files and Paths"_s )
9765 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileExists, u"Files and Paths"_s )
9766 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileName, u"Files and Paths"_s )
9767 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsFile, u"Files and Paths"_s )
9768 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsDir, u"Files and Paths"_s )
9769 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFilePath, u"Files and Paths"_s )
9770 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSize, u"Files and Paths"_s )
9771
9772 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ), fcnExif, u"Files and Paths"_s )
9773 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnExifGeoTag, u"GeometryGroup"_s )
9774
9775 // hash
9776 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ), fcnGenericHash, u"Conversions"_s )
9777 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashMd5, u"Conversions"_s )
9778 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashSha256, u"Conversions"_s )
9779
9780 //base64
9781 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBase64, u"Conversions"_s )
9782 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnFromBase64, u"Conversions"_s )
9783
9784 // magnetic models
9785 << new QgsStaticExpressionFunction(
9786 u"magnetic_declination"_s,
9788 << QgsExpressionFunction::Parameter( u"model_name"_s )
9789 << QgsExpressionFunction::Parameter( u"date"_s )
9790 << QgsExpressionFunction::Parameter( u"latitude"_s )
9791 << QgsExpressionFunction::Parameter( u"longitude"_s )
9792 << QgsExpressionFunction::Parameter( u"height"_s )
9793 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9794 fcnMagneticDeclination,
9795 u"MagneticModels"_s
9796 )
9797 << new QgsStaticExpressionFunction(
9798 u"magnetic_inclination"_s,
9800 << QgsExpressionFunction::Parameter( u"model_name"_s )
9801 << QgsExpressionFunction::Parameter( u"date"_s )
9802 << QgsExpressionFunction::Parameter( u"latitude"_s )
9803 << QgsExpressionFunction::Parameter( u"longitude"_s )
9804 << QgsExpressionFunction::Parameter( u"height"_s )
9805 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9806 fcnMagneticInclination,
9807 u"MagneticModels"_s
9808 )
9809 << new QgsStaticExpressionFunction(
9810 u"magnetic_declination_rate_of_change"_s,
9812 << QgsExpressionFunction::Parameter( u"model_name"_s )
9813 << QgsExpressionFunction::Parameter( u"date"_s )
9814 << QgsExpressionFunction::Parameter( u"latitude"_s )
9815 << QgsExpressionFunction::Parameter( u"longitude"_s )
9816 << QgsExpressionFunction::Parameter( u"height"_s )
9817 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9818 fcnMagneticDeclinationRateOfChange,
9819 u"MagneticModels"_s
9820 )
9821 << new QgsStaticExpressionFunction(
9822 u"magnetic_inclination_rate_of_change"_s,
9824 << QgsExpressionFunction::Parameter( u"model_name"_s )
9825 << QgsExpressionFunction::Parameter( u"date"_s )
9826 << QgsExpressionFunction::Parameter( u"latitude"_s )
9827 << QgsExpressionFunction::Parameter( u"longitude"_s )
9828 << QgsExpressionFunction::Parameter( u"height"_s )
9829 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9830 fcnMagneticInclinationRateOfChange,
9831 u"MagneticModels"_s
9832 )
9833
9834 // deprecated stuff - hidden from users
9835 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9836
9837 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9838 geomFunc->setIsStatic( false );
9839 functions << geomFunc;
9840
9841 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9842 areaFunc->setIsStatic( false );
9843 functions << areaFunc;
9844
9845 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9846
9847 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9848 lengthFunc->setIsStatic( false );
9849 functions << lengthFunc;
9850
9851 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9852 perimeterFunc->setIsStatic( false );
9853 functions << perimeterFunc;
9854
9855 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9856
9857 functions << new QgsStaticExpressionFunction( u"roundness"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnRoundness, u"GeometryGroup"_s );
9858
9859 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9860 xFunc->setIsStatic( false );
9861 functions << xFunc;
9862
9863 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9864 yFunc->setIsStatic( false );
9865 functions << yFunc;
9866
9867 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9868 zFunc->setIsStatic( false );
9869 functions << zFunc;
9870
9871 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions {
9872 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9873 { u"overlay_contains"_s, fcnGeomOverlayContains },
9874 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9875 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9876 { u"overlay_equals_exact"_s, fcnGeomOverlayEqualsExact },
9877 { u"overlay_equals_topological"_s, fcnGeomOverlayEqualsTopological },
9878 { u"overlay_equals_fuzzy"_s, fcnGeomOverlayEqualsFuzzy },
9879 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9880 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9881 { u"overlay_within"_s, fcnGeomOverlayWithin },
9882 };
9883 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9884 while ( i.hasNext() )
9885 {
9886 i.next();
9887 QString defaultBackend = i.key() == "overlay_equals"_L1 ? QString( "QGIS" ) : QString( "GEOS" );
9888 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction(
9889 i.key(),
9891 << QgsExpressionFunction::Parameter( u"layer"_s )
9892 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9893 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9894 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9895 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9896 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9897 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9898 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9899 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false )
9900 << QgsExpressionFunction::Parameter( u"backend"_s, true, defaultBackend, false )
9901 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4, false ),
9902 i.value(),
9903 u"GeometryGroup"_s,
9904 QString(),
9905 true,
9906 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9907 true
9908 );
9909
9910 // The current feature is accessed for the geometry, so this should not be cached
9911 fcnGeomOverlayFunc->setIsStatic( false );
9912 functions << fcnGeomOverlayFunc;
9913 }
9914
9915 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction(
9916 u"overlay_nearest"_s,
9918 << QgsExpressionFunction::Parameter( u"layer"_s )
9919 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9920 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9921 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9922 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9923 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9924 fcnGeomOverlayNearest,
9925 u"GeometryGroup"_s,
9926 QString(),
9927 true,
9928 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9929 true
9930 );
9931 // The current feature is accessed for the geometry, so this should not be cached
9932 fcnGeomOverlayNearestFunc->setIsStatic( false );
9933 functions << fcnGeomOverlayNearestFunc;
9934
9935 functions
9936 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9937 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9938 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
9939 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
9940 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
9941 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
9942 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
9943 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
9944 << new 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 )
9945 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
9946 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
9947 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
9948 << new 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 )
9949 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
9950 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
9951 << new QgsStaticExpressionFunction(
9952 u"make_triangle"_s,
9953 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ) << QgsExpressionFunction::Parameter( u"point3"_s ),
9954 fcnMakeTriangle,
9955 u"GeometryGroup"_s
9956 )
9957 << new QgsStaticExpressionFunction(
9958 u"make_circle"_s,
9960 << QgsExpressionFunction::Parameter( u"center"_s )
9961 << QgsExpressionFunction::Parameter( u"radius"_s )
9962 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9963 fcnMakeCircle,
9964 u"GeometryGroup"_s
9965 )
9966 << new QgsStaticExpressionFunction(
9967 u"make_ellipse"_s,
9969 << QgsExpressionFunction::Parameter( u"center"_s )
9970 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
9971 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
9972 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9973 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9974 fcnMakeEllipse,
9975 u"GeometryGroup"_s
9976 )
9977 << new QgsStaticExpressionFunction(
9978 u"make_regular_polygon"_s,
9980 << QgsExpressionFunction::Parameter( u"center"_s )
9981 << QgsExpressionFunction::Parameter( u"radius"_s )
9982 << QgsExpressionFunction::Parameter( u"number_sides"_s )
9983 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
9984 fcnMakeRegularPolygon,
9985 u"GeometryGroup"_s
9986 )
9987 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnMakeSquare, u"GeometryGroup"_s )
9988 << new QgsStaticExpressionFunction(
9989 u"make_rectangle_3points"_s,
9991 << QgsExpressionFunction::Parameter( u"point1"_s )
9992 << QgsExpressionFunction::Parameter( u"point2"_s )
9993 << QgsExpressionFunction::Parameter( u"point3"_s )
9994 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
9995 fcnMakeRectangleFrom3Points,
9996 u"GeometryGroup"_s
9997 )
9998 << new QgsStaticExpressionFunction(
9999 u"make_valid"_s,
10001 QgsExpressionFunction::Parameter( u"geometry"_s ),
10002#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
10003 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
10004#else
10005 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
10006#endif
10007 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
10008 },
10009 fcnGeomMakeValid,
10010 u"GeometryGroup"_s
10011 );
10012
10013 functions
10014 << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
10015 functions
10016 << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
10017 functions
10018 << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
10019 functions
10020 << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
10021
10022 QgsStaticExpressionFunction *xAtFunc
10023 = new QgsStaticExpressionFunction( u"$x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnOldXat, u"GeometryGroup"_s, QString(), true, QSet<QString>(), false, QStringList() << u"xat"_s );
10024 xAtFunc->setIsStatic( false );
10025 functions << xAtFunc;
10026
10027
10028 QgsStaticExpressionFunction *yAtFunc
10029 = new QgsStaticExpressionFunction( u"$y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnOldYat, u"GeometryGroup"_s, QString(), true, QSet<QString>(), false, QStringList() << u"yat"_s );
10030 yAtFunc->setIsStatic( false );
10031 functions << yAtFunc;
10032
10033 functions
10034 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
10035 << new QgsStaticExpressionFunction( u"x_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnXMin, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"xmin"_s )
10036 << new QgsStaticExpressionFunction( u"x_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnXMax, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"xmax"_s )
10037 << new QgsStaticExpressionFunction( u"y_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnYMin, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"ymin"_s )
10038 << new QgsStaticExpressionFunction( u"y_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnYMax, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"ymax"_s )
10039 << new QgsStaticExpressionFunction( u"geom_from_wkt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s ), fcnGeomFromWKT, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomFromWKT"_s )
10040 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10041 << new QgsStaticExpressionFunction( u"geom_from_gml"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"gml"_s ), fcnGeomFromGML, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"geomFromGML"_s )
10042 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
10043 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
10044 << new QgsStaticExpressionFunction(
10045 u"intersects_bbox"_s,
10046 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10047 fcnBbox,
10048 u"GeometryGroup"_s,
10049 QString(),
10050 false,
10051 QSet<QString>(),
10052 false,
10053 QStringList() << u"bbox"_s
10054 )
10055 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDisjoint, u"GeometryGroup"_s )
10056 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersects, u"GeometryGroup"_s )
10057 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnTouches, u"GeometryGroup"_s )
10058 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCrosses, u"GeometryGroup"_s )
10059 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnContains, u"GeometryGroup"_s )
10060 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnOverlaps, u"GeometryGroup"_s )
10061 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnWithin, u"GeometryGroup"_s )
10062 << new QgsStaticExpressionFunction( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnEquals, u"GeometryGroup"_s )
10063 << new QgsStaticExpressionFunction(
10064 u"equals_exact"_s,
10066 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10067 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10068 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s ),
10069 fcnIsEqualsExact,
10070 u"GeometryGroup"_s
10071 )
10072 << new QgsStaticExpressionFunction(
10073 u"equals_topological"_s,
10075 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10076 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10077 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"GEOS"_s ),
10078 fcnIsEqualsTopological,
10079 u"GeometryGroup"_s
10080 )
10081 << new QgsStaticExpressionFunction(
10082 u"equals_fuzzy"_s,
10084 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10085 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10086 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s )
10087 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4 ),
10088 fcnIsEqualsFuzzy,
10089 u"GeometryGroup"_s
10090 )
10091 << new QgsStaticExpressionFunction( u"translate"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnTranslate, u"GeometryGroup"_s )
10092 << new QgsStaticExpressionFunction(
10093 u"rotate"_s,
10095 << QgsExpressionFunction::Parameter( u"geometry"_s )
10096 << QgsExpressionFunction::Parameter( u"rotation"_s )
10097 << QgsExpressionFunction::Parameter( u"center"_s, true )
10098 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
10099 fcnRotate,
10100 u"GeometryGroup"_s
10101 )
10102 << new QgsStaticExpressionFunction(
10103 u"scale"_s,
10105 << QgsExpressionFunction::Parameter( u"geometry"_s )
10106 << QgsExpressionFunction::Parameter( u"x_scale"_s )
10107 << QgsExpressionFunction::Parameter( u"y_scale"_s )
10108 << QgsExpressionFunction::Parameter( u"center"_s, true ),
10109 fcnScale,
10110 u"GeometryGroup"_s
10111 )
10112 << new QgsStaticExpressionFunction(
10113 u"affine_transform"_s,
10115 << QgsExpressionFunction::Parameter( u"geometry"_s )
10116 << QgsExpressionFunction::Parameter( u"delta_x"_s )
10117 << QgsExpressionFunction::Parameter( u"delta_y"_s )
10118 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
10119 << QgsExpressionFunction::Parameter( u"scale_x"_s )
10120 << QgsExpressionFunction::Parameter( u"scale_y"_s )
10121 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
10122 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
10123 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
10124 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
10125 fcnAffineTransform,
10126 u"GeometryGroup"_s
10127 )
10128 << new QgsStaticExpressionFunction(
10129 u"buffer"_s,
10131 << QgsExpressionFunction::Parameter( u"geometry"_s )
10132 << QgsExpressionFunction::Parameter( u"distance"_s )
10133 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
10134 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
10135 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
10136 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
10137 fcnBuffer,
10138 u"GeometryGroup"_s
10139 )
10140 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForceRHR, u"GeometryGroup"_s )
10141 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCW, u"GeometryGroup"_s )
10142 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCCW, u"GeometryGroup"_s )
10143 << new QgsStaticExpressionFunction(
10144 u"wedge_buffer"_s,
10146 << QgsExpressionFunction::Parameter( u"center"_s )
10147 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10148 << QgsExpressionFunction::Parameter( u"width"_s )
10149 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
10150 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ),
10151 fcnWedgeBuffer,
10152 u"GeometryGroup"_s
10153 )
10154 << new QgsStaticExpressionFunction(
10155 u"tapered_buffer"_s,
10157 << QgsExpressionFunction::Parameter( u"geometry"_s )
10158 << QgsExpressionFunction::Parameter( u"start_width"_s )
10159 << QgsExpressionFunction::Parameter( u"end_width"_s )
10160 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ),
10161 fcnTaperedBuffer,
10162 u"GeometryGroup"_s
10163 )
10164 << new 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 )
10165 << new QgsStaticExpressionFunction(
10166 u"offset_curve"_s,
10168 << QgsExpressionFunction::Parameter( u"geometry"_s )
10169 << QgsExpressionFunction::Parameter( u"distance"_s )
10170 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10171 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10172 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10173 fcnOffsetCurve,
10174 u"GeometryGroup"_s
10175 )
10176 << new QgsStaticExpressionFunction(
10177 u"single_sided_buffer"_s,
10179 << QgsExpressionFunction::Parameter( u"geometry"_s )
10180 << QgsExpressionFunction::Parameter( u"distance"_s )
10181 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10182 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10183 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10184 fcnSingleSidedBuffer,
10185 u"GeometryGroup"_s
10186 )
10187 << new QgsStaticExpressionFunction(
10188 u"extend"_s,
10189 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10190 fcnExtend,
10191 u"GeometryGroup"_s
10192 )
10193 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
10194 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
10195 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
10196 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
10197 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
10198 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnInteriorRingN, u"GeometryGroup"_s )
10199 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnGeometryN, u"GeometryGroup"_s )
10200 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
10201 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
10202 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry1"_s ), QgsExpressionFunction::Parameter( u"geometry2"_s ) }, fcnSharedPaths, u"GeometryGroup"_s )
10203 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
10204 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
10205 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
10206 << new QgsStaticExpressionFunction(
10207 u"smooth"_s,
10209 << QgsExpressionFunction::Parameter( u"geometry"_s )
10210 << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
10211 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
10212 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
10213 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ),
10214 fcnSmooth,
10215 u"GeometryGroup"_s
10216 )
10217 << new QgsStaticExpressionFunction(
10218 u"triangular_wave"_s,
10219 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10220 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10221 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10222 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10223 fcnTriangularWave,
10224 u"GeometryGroup"_s
10225 )
10226 << new QgsStaticExpressionFunction(
10227 u"triangular_wave_randomized"_s,
10228 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10229 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10230 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10231 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10232 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10233 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10234 fcnTriangularWaveRandomized,
10235 u"GeometryGroup"_s
10236 )
10237 << new QgsStaticExpressionFunction(
10238 u"square_wave"_s,
10239 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10240 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10241 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10242 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10243 fcnSquareWave,
10244 u"GeometryGroup"_s
10245 )
10246 << new QgsStaticExpressionFunction(
10247 u"square_wave_randomized"_s,
10248 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10249 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10250 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10251 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10252 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10253 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10254 fcnSquareWaveRandomized,
10255 u"GeometryGroup"_s
10256 )
10257 << new QgsStaticExpressionFunction(
10258 u"wave"_s,
10259 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10260 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10261 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10262 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10263 fcnRoundWave,
10264 u"GeometryGroup"_s
10265 )
10266 << new QgsStaticExpressionFunction(
10267 u"wave_randomized"_s,
10268 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10269 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10270 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10271 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10272 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10273 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10274 fcnRoundWaveRandomized,
10275 u"GeometryGroup"_s
10276 )
10277 << new QgsStaticExpressionFunction(
10278 u"apply_dash_pattern"_s,
10279 {
10280 QgsExpressionFunction::Parameter( u"geometry"_s ),
10281 QgsExpressionFunction::Parameter( u"pattern"_s ),
10282 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
10283 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
10284 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
10285 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
10286 },
10287 fcnApplyDashPattern,
10288 u"GeometryGroup"_s
10289 )
10290 << new QgsStaticExpressionFunction( u"densify_by_count"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"vertices"_s ) }, fcnDensifyByCount, u"GeometryGroup"_s )
10291 << new QgsStaticExpressionFunction( u"densify_by_distance"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"distance"_s ) }, fcnDensifyByDistance, u"GeometryGroup"_s )
10292 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
10293 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
10294 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
10295 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
10296 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
10297 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
10298 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
10299 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
10300 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
10301 << new QgsStaticExpressionFunction(
10302 u"is_empty_or_null"_s,
10303 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
10304 fcnIsEmptyOrNull,
10305 u"GeometryGroup"_s,
10306 QString(),
10307 false,
10308 QSet<QString>(),
10309 false,
10310 QStringList(),
10311 true
10312 )
10313 << new QgsStaticExpressionFunction( u"convex_hull"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnConvexHull, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false, QStringList() << u"convexHull"_s )
10314#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
10315 << new QgsStaticExpressionFunction(
10316 u"concave_hull"_s,
10318 << QgsExpressionFunction::Parameter( u"geometry"_s )
10319 << QgsExpressionFunction::Parameter( u"target_percent"_s )
10320 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ),
10321 fcnConcaveHull,
10322 u"GeometryGroup"_s
10323 )
10324#endif
10325 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnOrientedBBox, u"GeometryGroup"_s )
10326 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMainAngle, u"GeometryGroup"_s )
10327 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ), fcnMinimalCircle, u"GeometryGroup"_s )
10328 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDifference, u"GeometryGroup"_s )
10329 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDistance, u"GeometryGroup"_s )
10330 << new QgsStaticExpressionFunction(
10331 u"hausdorff_distance"_s,
10333 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10334 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10335 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
10336 fcnHausdorffDistance,
10337 u"GeometryGroup"_s
10338 )
10339 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersection, u"GeometryGroup"_s )
10340 << new QgsStaticExpressionFunction(
10341 u"sym_difference"_s,
10342 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10343 fcnSymDifference,
10344 u"GeometryGroup"_s,
10345 QString(),
10346 false,
10347 QSet<QString>(),
10348 false,
10349 QStringList() << u"symDifference"_s
10350 )
10351 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10352 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10353 << new QgsStaticExpressionFunction(
10354 u"geom_to_wkt"_s,
10355 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
10356 fcnGeomToWKT,
10357 u"GeometryGroup"_s,
10358 QString(),
10359 false,
10360 QSet<QString>(),
10361 false,
10362 QStringList() << u"geomToWKT"_s
10363 )
10364 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10365 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
10366 << new QgsStaticExpressionFunction(
10367 u"transform"_s,
10368 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"source_auth_id"_s ) << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
10369 fcnTransformGeometry,
10370 u"GeometryGroup"_s
10371 )
10372 << new QgsStaticExpressionFunction(
10373 u"extrude"_s,
10374 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ),
10375 fcnExtrude,
10376 u"GeometryGroup"_s,
10377 QString()
10378 )
10379 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsMultipart, u"GeometryGroup"_s )
10380 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMax, u"GeometryGroup"_s )
10381 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMin, u"GeometryGroup"_s )
10382 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMax, u"GeometryGroup"_s )
10383 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMin, u"GeometryGroup"_s )
10384 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSinuosity, u"GeometryGroup"_s )
10385 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStraightDistance2d, u"GeometryGroup"_s );
10386
10387
10388 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction(
10389 u"order_parts"_s,
10391 << QgsExpressionFunction::Parameter( u"geometry"_s )
10392 << QgsExpressionFunction::Parameter( u"orderby"_s )
10393 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
10394 fcnOrderParts,
10395 u"GeometryGroup"_s,
10396 QString()
10397 );
10398
10399 orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10400 const QList< QgsExpressionNode *> argList = node->args()->list();
10401 for ( QgsExpressionNode *argNode : argList )
10402 {
10403 if ( !argNode->isStatic( parent, context ) )
10404 return false;
10405 }
10406
10407 if ( node->args()->count() > 1 )
10408 {
10409 QgsExpressionNode *argNode = node->args()->at( 1 );
10410
10411 QString expString = argNode->eval( parent, context ).toString();
10412
10413 QgsExpression e( expString );
10414
10415 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10416 return true;
10417 }
10418
10419 return true;
10420 } );
10421
10422 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10423 if ( node->args()->count() > 1 )
10424 {
10425 QgsExpressionNode *argNode = node->args()->at( 1 );
10426 QString expression = argNode->eval( parent, context ).toString();
10428 e.prepare( context );
10429 context->setCachedValue( expression, QVariant::fromValue( e ) );
10430 }
10431 return true;
10432 } );
10433 functions << orderPartsFunc;
10434
10435 functions
10436 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnClosestPoint, u"GeometryGroup"_s )
10437 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnShortestLine, u"GeometryGroup"_s )
10438 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
10439 << new QgsStaticExpressionFunction(
10440 u"line_interpolate_point_by_m"_s,
10442 << QgsExpressionFunction::Parameter( u"geometry"_s )
10443 << QgsExpressionFunction::Parameter( u"m"_s )
10444 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10445 fcnLineInterpolatePointByM,
10446 u"GeometryGroup"_s
10447 )
10448 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
10449 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
10450 << new QgsStaticExpressionFunction(
10451 u"line_locate_m"_s,
10453 << QgsExpressionFunction::Parameter( u"geometry"_s )
10454 << QgsExpressionFunction::Parameter( u"m"_s )
10455 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10456 fcnLineLocateM,
10457 u"GeometryGroup"_s
10458 )
10459 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
10460 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
10461 << new QgsStaticExpressionFunction(
10462 u"line_substring"_s,
10463 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10464 fcnLineSubset,
10465 u"GeometryGroup"_s
10466 );
10467
10468
10469 // **Record** functions
10470
10471 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
10472 idFunc->setIsStatic( false );
10473 functions << idFunc;
10474
10475 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
10476 currentFeatureFunc->setIsStatic( false );
10477 functions << currentFeatureFunc;
10478
10479 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction(
10480 u"uuid"_s,
10481 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ),
10482 fcnUuid,
10483 u"Record and Attributes"_s,
10484 QString(),
10485 false,
10486 QSet<QString>(),
10487 false,
10488 QStringList() << u"$uuid"_s
10489 );
10490 uuidFunc->setIsStatic( false );
10491 functions << uuidFunc;
10492
10493 functions
10494 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
10495 << new QgsStaticExpressionFunction(
10496 u"get_feature"_s,
10497 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"value"_s, true ),
10498 fcnGetFeature,
10499 u"Record and Attributes"_s,
10500 QString(),
10501 false,
10502 QSet<QString>(),
10503 false,
10504 QStringList() << u"QgsExpressionUtils::getFeature"_s
10505 )
10506 << new QgsStaticExpressionFunction(
10507 u"get_feature_by_id"_s,
10508 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"feature_id"_s ),
10509 fcnGetFeatureById,
10510 u"Record and Attributes"_s,
10511 QString(),
10512 false,
10513 QSet<QString>(),
10514 false
10515 );
10516
10517 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction(
10518 u"attributes"_s,
10519 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
10520 fcnAttributes,
10521 u"Record and Attributes"_s,
10522 QString(),
10523 false,
10524 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10525 );
10526 attributesFunc->setIsStatic( false );
10527 functions << attributesFunc;
10528 QgsStaticExpressionFunction *representAttributesFunc
10529 = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1, fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10530 representAttributesFunc->setIsStatic( false );
10531 functions << representAttributesFunc;
10532
10533 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction(
10534 u"is_feature_valid"_s,
10536 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10537 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10538 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10539 fcnValidateFeature,
10540 u"Record and Attributes"_s,
10541 QString(),
10542 false,
10543 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10544 );
10545 validateFeature->setIsStatic( false );
10546 functions << validateFeature;
10547
10548 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction(
10549 u"is_attribute_valid"_s,
10551 << QgsExpressionFunction::Parameter( u"attribute"_s, false )
10553 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10554 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10555 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10556 fcnValidateAttribute,
10557 u"Record and Attributes"_s,
10558 QString(),
10559 false,
10560 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10561 );
10562 validateAttribute->setIsStatic( false );
10563 functions << validateAttribute;
10564
10565 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction( u"maptip"_s, -1, fcnFeatureMaptip, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10566 maptipFunc->setIsStatic( false );
10567 functions << maptipFunc;
10568
10569 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction( u"display_expression"_s, -1, fcnFeatureDisplayExpression, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10570 displayFunc->setIsStatic( false );
10571 functions << displayFunc;
10572
10573 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( u"is_selected"_s, -1, fcnIsSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10574 isSelectedFunc->setIsStatic( false );
10575 functions << isSelectedFunc;
10576
10577 functions << new QgsStaticExpressionFunction( u"num_selected"_s, -1, fcnNumSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10578
10579 functions << new QgsStaticExpressionFunction(
10580 u"sqlite_fetch_and_increment"_s,
10582 << QgsExpressionFunction::Parameter( u"database"_s )
10583 << QgsExpressionFunction::Parameter( u"table"_s )
10584 << QgsExpressionFunction::Parameter( u"id_field"_s )
10585 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
10586 << QgsExpressionFunction::Parameter( u"filter_value"_s )
10587 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
10588 fcnSqliteFetchAndIncrement,
10589 u"Record and Attributes"_s
10590 );
10591
10592 // **CRS** functions
10593 functions
10594 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
10595 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
10596
10597
10598 // **Fields and Values** functions
10599 QgsStaticExpressionFunction *representValueFunc
10600 = new QgsStaticExpressionFunction( u"represent_value"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"field_name"_s, true ), fcnRepresentValue, u"Record and Attributes"_s );
10601
10602 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10603 Q_UNUSED( context )
10604 if ( node->args()->count() == 1 )
10605 {
10606 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
10607 if ( colRef )
10608 {
10609 return true;
10610 }
10611 else
10612 {
10613 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
10614 return false;
10615 }
10616 }
10617 else if ( node->args()->count() == 2 )
10618 {
10619 return true;
10620 }
10621 else
10622 {
10623 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
10624 return false;
10625 }
10626 } );
10627
10628 functions << representValueFunc;
10629
10630 // **General** functions
10631 functions
10632 << new QgsStaticExpressionFunction( u"layer_property"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"property"_s ), fcnGetLayerProperty, u"Map Layers"_s )
10633 << new QgsStaticExpressionFunction( u"decode_uri"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"part"_s, true ), fcnDecodeUri, u"Map Layers"_s )
10634 << new QgsStaticExpressionFunction( u"mime_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary_data"_s ), fcnMimeType, u"General"_s )
10635 << new QgsStaticExpressionFunction(
10636 u"raster_statistic"_s,
10637 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"statistic"_s ),
10638 fcnGetRasterBandStat,
10639 u"Rasters"_s
10640 );
10641
10642 // **var** function
10643 QgsStaticExpressionFunction *varFunction
10644 = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
10645 varFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10646 /* A variable node is static if it has a static name and the name can be found at prepare
10647 * time and is tagged with isStatic.
10648 * It is not static if a variable is set during iteration or not tagged isStatic.
10649 * (e.g. geom_part variable)
10650 */
10651 if ( node->args()->count() > 0 )
10652 {
10653 QgsExpressionNode *argNode = node->args()->at( 0 );
10654
10655 if ( !argNode->isStatic( parent, context ) )
10656 return false;
10657
10658 const QString varName = argNode->eval( parent, context ).toString();
10659 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
10660 return false;
10661
10662 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
10663 return scope ? scope->isStatic( varName ) : false;
10664 }
10665 return false;
10666 } );
10667 varFunction->setUsesGeometryFunction( []( const QgsExpressionNodeFunction *node ) -> bool {
10668 if ( node && node->args()->count() > 0 )
10669 {
10670 QgsExpressionNode *argNode = node->args()->at( 0 );
10671 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
10672 {
10673 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
10674 return true;
10675 }
10676 }
10677 return false;
10678 } );
10679
10680 functions << varFunction;
10681
10682 QgsStaticExpressionFunction *evalTemplateFunction
10683 = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10684 evalTemplateFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10685 if ( node->args()->count() > 0 )
10686 {
10687 QgsExpressionNode *argNode = node->args()->at( 0 );
10688
10689 if ( argNode->isStatic( parent, context ) )
10690 {
10691 QString expString = argNode->eval( parent, context ).toString();
10692
10693 QgsExpression e( expString );
10694
10695 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10696 return true;
10697 }
10698 }
10699
10700 return false;
10701 } );
10702 functions << evalTemplateFunction;
10703
10704 QgsStaticExpressionFunction *evalFunc
10705 = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10706 evalFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10707 if ( node->args()->count() > 0 )
10708 {
10709 QgsExpressionNode *argNode = node->args()->at( 0 );
10710
10711 if ( argNode->isStatic( parent, context ) )
10712 {
10713 QString expString = argNode->eval( parent, context ).toString();
10714
10715 QgsExpression e( expString );
10716
10717 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10718 return true;
10719 }
10720 }
10721
10722 return false;
10723 } );
10724
10725 functions << evalFunc;
10726
10727 QgsStaticExpressionFunction *attributeFunc
10728 = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10729 attributeFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10730 const QList< QgsExpressionNode *> argList = node->args()->list();
10731 for ( QgsExpressionNode *argNode : argList )
10732 {
10733 if ( !argNode->isStatic( parent, context ) )
10734 return false;
10735 }
10736
10737 if ( node->args()->count() == 1 )
10738 {
10739 // not static -- this is the variant which uses the current feature taken direct from the expression context
10740 return false;
10741 }
10742
10743 return true;
10744 } );
10745 functions << attributeFunc;
10746
10747 functions
10748 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10749 << new QgsWithVariableExpressionFunction()
10750 << new QgsStaticExpressionFunction(
10751 u"raster_value"_s,
10752 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10753 fcnRasterValue,
10754 u"Rasters"_s
10755 )
10756 << new QgsStaticExpressionFunction(
10757 u"raster_attributes"_s,
10758 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10759 fcnRasterAttributes,
10760 u"Rasters"_s
10761 )
10762
10763 // functions for arrays
10764 << new QgsArrayForeachExpressionFunction()
10765 << new QgsArrayFilterExpressionFunction()
10766 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10767 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10768 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10769 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10770 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10771 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayAll, u"Arrays"_s )
10772 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10773 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10774 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10775 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10776 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10777 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10778 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10779 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10780 << new QgsStaticExpressionFunction( u"array_majority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMajority, u"Arrays"_s )
10781 << new QgsStaticExpressionFunction( u"array_minority"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"option"_s, true, QVariant( "all" ) ), fcnArrayMinority, u"Arrays"_s )
10782 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10783 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10784 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10785 << new QgsStaticExpressionFunction(
10786 u"array_insert"_s,
10787 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10788 fcnArrayInsert,
10789 u"Arrays"_s
10790 )
10791 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10792 << new QgsStaticExpressionFunction(
10793 u"array_remove_all"_s,
10794 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10795 fcnArrayRemoveAll,
10796 u"Arrays"_s,
10797 QString(),
10798 false,
10799 QSet<QString>(),
10800 false,
10801 QStringList(),
10802 true
10803 )
10804 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10805 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"priority"_s ), fcnArrayPrioritize, u"Arrays"_s )
10806 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10807 << new QgsStaticExpressionFunction(
10808 u"array_slice"_s,
10809 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ),
10810 fcnArraySlice,
10811 u"Arrays"_s
10812 )
10813 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10814 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10815 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10816 << new QgsStaticExpressionFunction(
10817 u"array_to_string"_s,
10819 << QgsExpressionFunction::Parameter( u"array"_s )
10820 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10821 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10822 fcnArrayToString,
10823 u"Arrays"_s
10824 )
10825 << new QgsStaticExpressionFunction(
10826 u"string_to_array"_s,
10828 << QgsExpressionFunction::Parameter( u"string"_s )
10829 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10830 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10831 fcnStringToArray,
10832 u"Arrays"_s
10833 )
10834 << new QgsStaticExpressionFunction(
10835 u"generate_series"_s,
10836 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ),
10837 fcnGenerateSeries,
10838 u"Arrays"_s
10839 )
10840 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10841
10842 //functions for maps
10843 << new QgsStaticExpressionFunction( u"from_json"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLoadJson, u"Maps"_s, QString(), false, QSet<QString>(), false, QStringList() << u"json_to_map"_s )
10844 << new QgsStaticExpressionFunction( u"to_json"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"json_string"_s ), fcnWriteJson, u"Maps"_s, QString(), false, QSet<QString>(), false, QStringList() << u"map_to_json"_s )
10845 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10846 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10847 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10848 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10849 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10850 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10851 << new QgsStaticExpressionFunction( u"map_insert"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnMapInsert, u"Maps"_s )
10852 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10853 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10854 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10855 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"prefix"_s ), fcnMapPrefixKeys, u"Maps"_s )
10856 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlTable, u"Maps"_s )
10857 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlDefinitionList, u"Maps"_s )
10858 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnToFormUrlEncode, u"Maps"_s )
10859
10860 ;
10861
10863
10864 //QgsExpression has ownership of all built-in functions
10865 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10866 {
10867 *sOwnedFunctions() << func;
10868 *sBuiltinFunctions() << func->name();
10869 sBuiltinFunctions()->append( func->aliases() );
10870 }
10871 }
10872 return functions;
10873}
10874
10875bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10876{
10877 int fnIdx = functionIndex( function->name() );
10878 if ( fnIdx != -1 )
10879 {
10880 return false;
10881 }
10882
10883 QMutexLocker locker( &sFunctionsMutex );
10884 sFunctions()->append( function );
10885 if ( transferOwnership )
10886 sOwnedFunctions()->append( function );
10887
10888 return true;
10889}
10890
10891bool QgsExpression::unregisterFunction( const QString &name )
10892{
10893 // You can never override the built in functions.
10894 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10895 {
10896 return false;
10897 }
10898 int fnIdx = functionIndex( name );
10899 if ( fnIdx != -1 )
10900 {
10901 QMutexLocker locker( &sFunctionsMutex );
10902 sFunctions()->removeAt( fnIdx );
10903 sFunctionIndexMap.clear();
10904 return true;
10905 }
10906 return false;
10907}
10908
10910{
10911 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10912 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10913 {
10914 sBuiltinFunctions()->removeAll( func->name() );
10915 for ( const QString &alias : func->aliases() )
10917 sBuiltinFunctions()->removeAll( alias );
10918 }
10919
10920 sFunctions()->removeAll( func );
10921 }
10922
10923 qDeleteAll( *sOwnedFunctions() );
10924 sOwnedFunctions()->clear();
10926
10927const QStringList &QgsExpression::BuiltinFunctions()
10928{
10929 if ( sBuiltinFunctions()->isEmpty() )
10930 {
10931 Functions(); // this method builds the gmBuiltinFunctions as well
10932 }
10933 return *sBuiltinFunctions();
10934}
10935
10938 u"array_foreach"_s, // skip-keyword-check
10939 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ),
10940 u"Arrays"_s
10941 )
10942{}
10943
10945{
10946 bool isStatic = false;
10947
10948 QgsExpressionNode::NodeList *args = node->args();
10950 if ( args->count() < 2 )
10951 return false;
10952
10953 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10954 {
10955 isStatic = true;
10956 }
10957 return isStatic;
10958}
10959
10961{
10962 Q_UNUSED( node )
10963 QVariantList result;
10964
10965 if ( args->count() < 2 )
10966 // error
10967 return result;
10968
10969 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10970
10971 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10972 std::unique_ptr< QgsExpressionContext > tempContext;
10973 if ( !subContext )
10974 {
10975 tempContext = std::make_unique< QgsExpressionContext >();
10976 subContext = tempContext.get();
10977 }
10978
10979 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10980 subContext->appendScope( subScope );
10981
10982 int i = 0;
10983 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
10984 {
10985 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
10986 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
10987 result << args->at( 1 )->eval( parent, subContext );
10988 }
10989
10990 if ( context )
10991 delete subContext->popScope();
10992
10993 return result;
10994}
10995
10996QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10998 // This is a dummy function, all the real handling is in run
10999 Q_UNUSED( values )
11000 Q_UNUSED( context )
11001 Q_UNUSED( parent )
11002 Q_UNUSED( node )
11003
11004 Q_ASSERT( false );
11005 return QVariant();
11006}
11007
11009{
11010 QgsExpressionNode::NodeList *args = node->args();
11011
11012 if ( args->count() < 2 )
11013 // error
11014 return false;
11015
11016 args->at( 0 )->prepare( parent, context );
11017
11018 QgsExpressionContext subContext;
11019 if ( context )
11020 subContext = *context;
11023 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11024 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
11025 subContext.appendScope( subScope );
11026
11027 args->at( 1 )->prepare( parent, &subContext );
11028
11029 return true;
11030}
11031
11033 : 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 )
11034{}
11035
11037{
11038 bool isStatic = false;
11039
11040 QgsExpressionNode::NodeList *args = node->args();
11042 if ( args->count() < 2 )
11043 return false;
11044
11045 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11046 {
11047 isStatic = true;
11048 }
11049 return isStatic;
11050}
11051
11053{
11054 Q_UNUSED( node )
11055 QVariantList result;
11056
11057 if ( args->count() < 2 )
11058 // error
11059 return result;
11060
11061 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11062
11063 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11064 std::unique_ptr< QgsExpressionContext > tempContext;
11065 if ( !subContext )
11066 {
11067 tempContext = std::make_unique< QgsExpressionContext >();
11068 subContext = tempContext.get();
11069 }
11070
11071 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11072 subContext->appendScope( subScope );
11073
11074 int limit = 0;
11075 if ( args->count() >= 3 )
11076 {
11077 const QVariant limitVar = args->at( 2 )->eval( parent, context );
11078
11079 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
11080 {
11081 limit = limitVar.toInt();
11082 }
11083 else
11084 {
11085 return result;
11086 }
11087 }
11088
11089 for ( const QVariant &value : array )
11090 {
11091 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
11092 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
11093 {
11094 result << value;
11095
11096 if ( limit > 0 && limit == result.size() )
11097 break;
11098 }
11099 }
11100
11101 if ( context )
11102 delete subContext->popScope();
11103
11104 return result;
11105}
11106
11107QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11109 // This is a dummy function, all the real handling is in run
11110 Q_UNUSED( values )
11111 Q_UNUSED( context )
11112 Q_UNUSED( parent )
11113 Q_UNUSED( node )
11114
11115 Q_ASSERT( false );
11116 return QVariant();
11117}
11118
11120{
11121 QgsExpressionNode::NodeList *args = node->args();
11122
11123 if ( args->count() < 2 )
11124 // error
11125 return false;
11126
11127 args->at( 0 )->prepare( parent, context );
11128
11129 QgsExpressionContext subContext;
11130 if ( context )
11131 subContext = *context;
11132
11134 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11135 subContext.appendScope( subScope );
11136
11137 args->at( 1 )->prepare( parent, &subContext );
11138
11139 return true;
11140}
11142 : 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 )
11143{}
11144
11146{
11147 bool isStatic = false;
11148
11149 QgsExpressionNode::NodeList *args = node->args();
11150
11151 if ( args->count() < 3 )
11152 return false;
11153
11154 // We only need to check if the node evaluation is static, if both - name and value - are static.
11155 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11156 {
11157 QVariant name = args->at( 0 )->eval( parent, context );
11158 QVariant value = args->at( 1 )->eval( parent, context );
11160 // Temporarily append a new scope to provide the variable
11161 appendTemporaryVariable( context, name.toString(), value );
11162 if ( args->at( 2 )->isStatic( parent, context ) )
11163 isStatic = true;
11164 popTemporaryVariable( context );
11165 }
11166
11167 return isStatic;
11168}
11169
11171{
11172 Q_UNUSED( node )
11173 QVariant result;
11174
11175 if ( args->count() < 3 )
11176 // error
11177 return result;
11178
11179 QVariant name = args->at( 0 )->eval( parent, context );
11180 QVariant value = args->at( 1 )->eval( parent, context );
11181
11182 const QgsExpressionContext *updatedContext = context;
11183 std::unique_ptr< QgsExpressionContext > tempContext;
11184 if ( !updatedContext )
11185 {
11186 tempContext = std::make_unique< QgsExpressionContext >();
11187 updatedContext = tempContext.get();
11189
11190 appendTemporaryVariable( updatedContext, name.toString(), value );
11191 result = args->at( 2 )->eval( parent, updatedContext );
11192
11193 if ( context )
11194 popTemporaryVariable( updatedContext );
11195
11196 return result;
11197}
11198
11199QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11201 // This is a dummy function, all the real handling is in run
11202 Q_UNUSED( values )
11203 Q_UNUSED( context )
11204 Q_UNUSED( parent )
11205 Q_UNUSED( node )
11206
11207 Q_ASSERT( false );
11208 return QVariant();
11209}
11210
11212{
11213 QgsExpressionNode::NodeList *args = node->args();
11214
11215 if ( args->count() < 3 )
11216 // error
11217 return false;
11218
11219 QVariant name = args->at( 0 )->prepare( parent, context );
11220 QVariant value = args->at( 1 )->prepare( parent, context );
11221
11222 const QgsExpressionContext *updatedContext = context;
11223 std::unique_ptr< QgsExpressionContext > tempContext;
11224 if ( !updatedContext )
11225 {
11226 tempContext = std::make_unique< QgsExpressionContext >();
11227 updatedContext = tempContext.get();
11228 }
11229
11230 appendTemporaryVariable( updatedContext, name.toString(), value );
11231 args->at( 2 )->prepare( parent, updatedContext );
11232
11233 if ( context )
11234 popTemporaryVariable( updatedContext );
11235
11236 return true;
11237}
11238
11239void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
11240{
11241 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11242 delete updatedContext->popScope();
11243}
11244
11245void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
11246{
11247 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
11248 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
11249
11250 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11251 updatedContext->appendScope( scope );
11252}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2268
@ GEOS
Use GEOS implementation.
Definition qgis.h:2270
@ QGIS
Use internal implementation.
Definition qgis.h:2269
@ Left
Buffer to left of line.
Definition qgis.h:2218
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3472
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3474
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3473
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3475
@ Success
Operation succeeded.
Definition qgis.h:2163
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3184
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2329
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
JoinStyle
Join styles for buffers.
Definition qgis.h:2242
@ Bevel
Use beveled joins.
Definition qgis.h:2245
@ Round
Use rounded joins.
Definition qgis.h:2243
@ Miter
Use mitered joins.
Definition qgis.h:2244
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6509
@ StdDev
Standard deviation.
Definition qgis.h:6516
@ NoStatistic
No statistic.
Definition qgis.h:6510
@ 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:2229
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2231
@ Round
Round cap.
Definition qgis.h:2230
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2232
Aggregate
Available aggregates to calculate.
Definition qgis.h:6386
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6403
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6400
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6393
@ Median
Median of values (numeric fields only).
Definition qgis.h:6394
@ Max
Max of values.
Definition qgis.h:6391
@ Min
Min of values.
Definition qgis.h:6390
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6404
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6397
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6408
@ Sum
Sum of values.
Definition qgis.h:6392
@ Minority
Minority of values.
Definition qgis.h:6398
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6389
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6407
@ Majority
Majority of values.
Definition qgis.h:6399
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6396
@ Count
Count.
Definition qgis.h:6387
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6401
@ CountDistinct
Number of distinct values.
Definition qgis.h:6388
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6405
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6406
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6402
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3457
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3460
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3462
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3461
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3459
@ NoRule
No special rule.
Definition qgis.h:3458
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2313
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2314
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2315
@ Point
Point.
Definition qgis.h:296
@ PointM
PointM.
Definition qgis.h:329
@ PointZ
PointZ.
Definition qgis.h:313
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ PointZM
PointZM.
Definition qgis.h:345
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()
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.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions).
friend class QgsExpressionNodeFunction
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsExpression(const QString &expr)
Creates a new expression based on the provided string.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h: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:749
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
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 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 extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
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:175
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2890
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:733
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:599
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:456
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:745
double y
Definition qgspoint.h:57
QgsRelationManager * relationManager
Definition qgsproject.h:125
static QgsProject * instance()
Returns the QgsProject singleton instance.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
bool isRamp() const
Returns true if the field carries a color ramp component information (RedMin/RedMax,...
bool isColor() const
Returns true if the field carries a color component (Red, Green, Blue and optionally Alpha) informati...
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
void grow(double delta)
Grows the rectangle in place by the specified amount.
double yMaximum
QgsPointXY center
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices).
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
Q_INVOKABLE QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString unaccent(const QString &input)
Removes accents and other diacritical marks from a string, replacing accented characters with their u...
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance).
Definition qgsstyle.cpp:541
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)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp: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:7423
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7766
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7765
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7262
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7222
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
const QString cacheKey(const QString &pathIn)
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
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)
QList< QgsExpressionFunction * > ExpressionFunctionList
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColorObject(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
#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.
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