QGIS API Documentation 4.1.0-Master (376402f9aeb)
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 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1513 return QVariant();
1514#endif
1515}
1516
1517static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1518{
1519#if QT_FEATURE_timezone > 0
1520 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1521 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1522 if ( datetime.isValid() && tz.isValid() )
1523 {
1524 datetime.setTimeZone( tz );
1525 return QVariant::fromValue( datetime );
1526 }
1527 return QVariant();
1528#else
1529 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1530 return QVariant();
1531#endif
1532}
1533
1534static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1535{
1536#if QT_FEATURE_timezone > 0
1537 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1538 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1539 if ( datetime.isValid() && tz.isValid() )
1540 {
1541 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1542 }
1543 return QVariant();
1544#else
1545 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1546 return QVariant();
1547#endif
1548}
1549
1550static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1551{
1552#if QT_FEATURE_timezone > 0
1553 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1554 if ( timeZone.isValid() )
1555 {
1556 return QString( timeZone.id() );
1557 }
1558 return QVariant();
1559#else
1560 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1561 return QVariant();
1562#endif
1563}
1564
1565static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1566{
1567 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1568 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1569 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1570 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1571 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1572 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1573 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1574
1575 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1576}
1577
1578static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1579{
1580 for ( const QVariant &value : values )
1581 {
1582 if ( QgsVariantUtils::isNull( value ) )
1583 continue;
1584 return value;
1585 }
1586 return QVariant();
1587}
1588
1589static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1590{
1591 const QVariant val1 = values.at( 0 );
1592 const QVariant val2 = values.at( 1 );
1593
1594 if ( val1 == val2 )
1595 return QVariant();
1596 else
1597 return val1;
1598}
1599
1600static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1601{
1602 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1603 return QVariant( str.toLower() );
1604}
1605static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1606{
1607 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1608 return QVariant( str.toUpper() );
1609}
1610static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1611{
1612 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1613 QStringList elems = str.split( ' ' );
1614 for ( int i = 0; i < elems.size(); i++ )
1615 {
1616 if ( elems[i].size() > 1 )
1617 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1618 }
1619 return QVariant( elems.join( ' '_L1 ) );
1620}
1621
1622static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1623{
1624 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1625 return QVariant( str.trimmed() );
1626}
1627
1628static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1629{
1630 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1631
1632 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1633
1634 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1635 str.replace( re, QString() );
1636 return QVariant( str );
1637}
1638
1639static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1640{
1641 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1642
1643 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1644
1645 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1646 str.replace( re, QString() );
1647 return QVariant( str );
1648}
1649
1650static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1651{
1652 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1653 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1654 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1655}
1656
1657static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1658{
1659 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1660 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1661 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1662}
1663
1664static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1665{
1666 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1667 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1668 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1669 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1670}
1671
1672static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1673{
1674 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1675 return QVariant( QgsStringUtils::soundex( string ) );
1676}
1677
1678static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1679{
1680 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1681 return QVariant( QString( character ) );
1682}
1683
1684static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1685{
1686 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1687
1688 if ( value.isEmpty() )
1689 {
1690 return QVariant();
1691 }
1692
1693 int res = value.at( 0 ).unicode();
1694 return QVariant( res );
1695}
1696
1697static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1698{
1699 if ( values.length() == 2 || values.length() == 3 )
1700 {
1701 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1702 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1703
1704 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1705
1706 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1707 }
1708
1709 return QVariant();
1710}
1711
1712static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1713{
1714 // two variants, one for geometry, one for string
1715
1716 //geometry variant
1717 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1718 if ( !geom.isNull() )
1719 {
1720 if ( geom.type() == Qgis::GeometryType::Line )
1721 return QVariant( geom.length() );
1722 else
1723 return QVariant();
1724 }
1725
1726 //otherwise fall back to string variant
1727 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1728 return QVariant( str.length() );
1729}
1730
1731static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1732{
1733 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1734
1735 if ( geom.type() != Qgis::GeometryType::Line )
1736 return QVariant();
1737
1738 double totalLength = 0;
1739 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1740 {
1742 {
1743 totalLength += line->length3D();
1744 }
1745 else
1746 {
1747 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1748 totalLength += segmentized->length3D();
1749 }
1750 }
1751
1752 return totalLength;
1753}
1754
1755
1756static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1757{
1758 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1759 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1760 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1761}
1762
1763static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1764{
1765 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1766 {
1767 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1768 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1769 QVector< QPair< QString, QString > > mapItems;
1770
1771 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1772 {
1773 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1774 }
1775
1776 // larger keys should be replaced first since they may contain whole smaller keys
1777 std::sort( mapItems.begin(), mapItems.end(), []( const QPair< QString, QString > &pair1, const QPair< QString, QString > &pair2 ) { return ( pair1.first.length() > pair2.first.length() ); } );
1778
1779 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1780 {
1781 str = str.replace( it->first, it->second );
1782 }
1783
1784 return QVariant( str );
1785 }
1786 else if ( values.count() == 3 )
1787 {
1788 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1789 QVariantList before;
1790 QVariantList after;
1791 bool isSingleReplacement = false;
1792
1793 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1794 {
1795 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1796 }
1797 else
1798 {
1799 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1800 }
1801
1802 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1803 {
1804 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1805 isSingleReplacement = true;
1806 }
1807 else
1808 {
1809 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1810 }
1811
1812 if ( !isSingleReplacement && before.length() != after.length() )
1813 {
1814 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1815 return QVariant();
1816 }
1817
1818 for ( int i = 0; i < before.length(); i++ )
1819 {
1820 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1821 }
1822
1823 return QVariant( str );
1824 }
1825 else
1826 {
1827 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1828 return QVariant();
1829 }
1830}
1831
1832static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1833{
1834 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1835 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1836 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1837
1838 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1839 if ( !re.isValid() )
1840 {
1841 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1842 return QVariant();
1843 }
1844 return QVariant( str.replace( re, after ) );
1845}
1846
1847static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1848{
1849 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1850 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1851
1852 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1853 if ( !re.isValid() )
1854 {
1855 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1856 return QVariant();
1857 }
1858 return QVariant( ( str.indexOf( re ) + 1 ) );
1859}
1860
1861static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1862{
1863 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1864 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1865 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1866
1867 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1868 if ( !re.isValid() )
1869 {
1870 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1871 return QVariant();
1872 }
1873
1874 QRegularExpressionMatch matches = re.match( str );
1875 if ( matches.hasMatch() )
1876 {
1877 QVariantList array;
1878 QStringList list = matches.capturedTexts();
1879
1880 // Skip the first string to only return captured groups
1881 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1882 {
1883 array += ( !( *it ).isEmpty() ) ? *it : empty;
1884 }
1885
1886 return QVariant( array );
1887 }
1888 else
1889 {
1890 return QVariant();
1891 }
1892}
1893
1894static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1895{
1896 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1897 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1898
1899 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1900 if ( !re.isValid() )
1901 {
1902 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1903 return QVariant();
1904 }
1905
1906 // extract substring
1907 QRegularExpressionMatch match = re.match( str );
1908 if ( match.hasMatch() )
1909 {
1910 // return first capture
1911 if ( match.lastCapturedIndex() > 0 )
1912 {
1913 // a capture group was present, so use that
1914 return QVariant( match.captured( 1 ) );
1915 }
1916 else
1917 {
1918 // no capture group, so using all match
1919 return QVariant( match.captured( 0 ) );
1920 }
1921 }
1922 else
1923 {
1924 return QVariant( "" );
1925 }
1926}
1927
1928static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1929{
1930 QString uuid = QUuid::createUuid().toString();
1931 if ( values.at( 0 ).toString().compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1932 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1933 else if ( values.at( 0 ).toString().compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1934 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1935 return uuid;
1936}
1937
1938static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1939{
1940 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1941 return QVariant();
1942
1943 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1944 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1945
1946 int len = 0;
1947 if ( values.at( 2 ).isValid() )
1948 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1949 else
1950 len = str.size();
1951
1952 if ( from < 0 )
1953 {
1954 from = str.size() + from;
1955 if ( from < 0 )
1956 {
1957 from = 0;
1958 }
1959 }
1960 else if ( from > 0 )
1961 {
1962 //account for the fact that substr() starts at 1
1963 from -= 1;
1964 }
1965
1966 if ( len < 0 )
1967 {
1968 len = str.size() + len - from;
1969 if ( len < 0 )
1970 {
1971 len = 0;
1972 }
1973 }
1974
1975 return QVariant( str.mid( from, len ) );
1976}
1977static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1978{
1979 FEAT_FROM_CONTEXT( context, f )
1980 return QVariant( f.id() );
1981}
1982
1983static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1984{
1985 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1986 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1987 bool foundLayer = false;
1988 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
1989 values.at( 0 ),
1990 context,
1991 parent,
1992 [parent, bandNb, geom]( QgsMapLayer *mapLayer ) {
1993 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1994 if ( !layer || !layer->dataProvider() )
1995 {
1996 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1997 return QVariant();
1998 }
1999
2000 if ( bandNb < 1 || bandNb > layer->bandCount() )
2001 {
2002 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
2003 return QVariant();
2004 }
2005
2006 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
2007 {
2008 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
2009 return QVariant();
2010 }
2011
2012 QgsPointXY point = geom.asPoint();
2013 if ( geom.isMultipart() )
2014 {
2015 QgsMultiPointXY multiPoint = geom.asMultiPoint();
2016 if ( multiPoint.count() == 1 )
2017 {
2018 point = multiPoint[0];
2019 }
2020 else
2021 {
2022 // if the geometry contains more than one part, return an undefined value
2023 return QVariant();
2024 }
2025 }
2026
2027 double value = layer->dataProvider()->sample( point, bandNb );
2028 return std::isnan( value ) ? QVariant() : value;
2029 },
2030 foundLayer
2031 );
2032
2033 if ( !foundLayer )
2034 {
2035 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2036 return QVariant();
2037 }
2038 else
2039 {
2040 return res;
2041 }
2042}
2043
2044static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2045{
2046 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2047 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2048
2049 bool foundLayer = false;
2050 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2051 values.at( 0 ),
2052 context,
2053 parent,
2054 [parent, bandNb, value]( QgsMapLayer *mapLayer ) -> QVariant {
2055 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
2056 if ( !layer || !layer->dataProvider() )
2057 {
2058 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2059 return QVariant();
2060 }
2061
2062 if ( bandNb < 1 || bandNb > layer->bandCount() )
2063 {
2064 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
2065 return QVariant();
2066 }
2067
2068 if ( std::isnan( value ) )
2069 {
2070 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
2071 return QVariant();
2072 }
2073
2074 if ( !layer->dataProvider()->attributeTable( bandNb ) )
2075 {
2076 return QVariant();
2077 }
2078
2079 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
2080 if ( data.isEmpty() )
2081 {
2082 return QVariant();
2083 }
2084
2085 QVariantMap result;
2086 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
2087 for ( int idx = 0; idx < static_cast<int>( fields.count() ) && idx < static_cast<int>( data.count() ); ++idx )
2088 {
2089 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
2090 if ( field.isColor() || field.isRamp() )
2091 {
2092 continue;
2093 }
2094 result.insert( fields.at( idx ).name, data.at( idx ) );
2095 }
2096
2097 return result;
2098 },
2099 foundLayer
2100 );
2101
2102 if ( !foundLayer )
2103 {
2104 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2105 return QVariant();
2106 }
2107 else
2108 {
2109 return res;
2110 }
2111}
2112
2113static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2114{
2115 if ( !context )
2116 return QVariant();
2117
2118 return context->feature();
2119}
2120
2121static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2122{
2123 QgsFeature feature;
2124 QString attr;
2125 if ( values.size() == 1 )
2126 {
2127 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2128 feature = context->feature();
2129 }
2130 else if ( values.size() == 2 )
2131 {
2132 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2133 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2134 }
2135 else
2136 {
2137 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2138 return QVariant();
2139 }
2140
2141 return feature.attribute( attr );
2142}
2143
2144static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2145{
2146 QString table { R"html(
2147 <table>
2148 <thead>
2149 <tr><th>%1</th></tr>
2150 </thead>
2151 <tbody>
2152 <tr><td>%2</td></tr>
2153 </tbody>
2154 </table>)html" };
2155 QVariantMap dict;
2156 if ( values.size() == 1 )
2157 {
2158 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2159 }
2160 else
2161 {
2162 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2163 return QVariant();
2164 }
2165
2166 if ( dict.isEmpty() )
2167 {
2168 return QVariant();
2169 }
2170
2171 QStringList headers;
2172 QStringList cells;
2173
2174 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2175 {
2176 headers.push_back( it.key().toHtmlEscaped() );
2177 cells.push_back( it.value().toString().toHtmlEscaped() );
2178 }
2179
2180 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2181}
2182
2183static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2184{
2185 QString table { R"html(
2186 <dl>
2187 %1
2188 </dl>)html" };
2189 QVariantMap dict;
2190 if ( values.size() == 1 )
2191 {
2192 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2193 }
2194 else
2195 {
2196 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2197 return QVariant();
2198 }
2199
2200 if ( dict.isEmpty() )
2201 {
2202 return QVariant();
2203 }
2204
2205 QString rows;
2206
2207 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2208 {
2209 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2210 }
2211
2212 return table.arg( rows );
2213}
2214
2215static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2216{
2217 QVariant layer;
2218 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2219 {
2220 layer = context->variable( u"layer"_s );
2221 }
2222 else
2223 {
2224 //first node is layer id or name
2225 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2227 layer = node->eval( parent, context );
2229 }
2230
2231 QgsFeature feature;
2232 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2233 {
2234 feature = context->feature();
2235 }
2236 else
2237 {
2238 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2239 }
2240
2242 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2243 if ( strength == "hard"_L1 )
2244 {
2246 }
2247 else if ( strength == "soft"_L1 )
2248 {
2250 }
2251
2252 bool foundLayer = false;
2253 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2254 layer,
2255 context,
2256 parent,
2257 [parent, feature, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2258 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2259 if ( !layer )
2260 {
2261 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2262 return QVariant();
2263 }
2264
2265 const QgsFields fields = layer->fields();
2266 bool valid = true;
2267 for ( int i = 0; i < fields.size(); i++ )
2268 {
2269 QStringList errors;
2270 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2271 if ( !valid )
2272 {
2273 break;
2274 }
2275 }
2276
2277 return valid;
2278 },
2279 foundLayer
2280 );
2281
2282 if ( !foundLayer )
2283 {
2284 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2285 return QVariant();
2286 }
2287
2288 return res;
2289}
2290
2291static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2292{
2293 QVariant layer;
2294 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2295 {
2296 layer = context->variable( u"layer"_s );
2297 }
2298 else
2299 {
2300 //first node is layer id or name
2301 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2303 layer = node->eval( parent, context );
2305 }
2306
2307 QgsFeature feature;
2308 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2309 {
2310 feature = context->feature();
2311 }
2312 else
2313 {
2314 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2315 }
2316
2318 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2319 if ( strength == "hard"_L1 )
2320 {
2322 }
2323 else if ( strength == "soft"_L1 )
2324 {
2326 }
2327
2328 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2329
2330 bool foundLayer = false;
2331 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2332 layer,
2333 context,
2334 parent,
2335 [parent, feature, attributeName, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2336 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2337 if ( !layer )
2338 {
2339 return QVariant();
2340 }
2341
2342 const int fieldIndex = layer->fields().indexFromName( attributeName );
2343 if ( fieldIndex == -1 )
2344 {
2345 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2346 return QVariant();
2347 }
2348
2349 QStringList errors;
2350 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2351 return valid;
2352 },
2353 foundLayer
2354 );
2355
2356 if ( !foundLayer )
2357 {
2358 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2359 return QVariant();
2360 }
2361
2362 return res;
2363}
2364
2365static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2366{
2367 QgsFeature feature;
2368 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2369 {
2370 feature = context->feature();
2371 }
2372 else
2373 {
2374 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2375 }
2376
2377 const QgsFields fields = feature.fields();
2378 QVariantMap result;
2379 for ( int i = 0; i < fields.count(); ++i )
2380 {
2381 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2382 }
2383 return result;
2384}
2385
2386static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2387{
2388 QgsVectorLayer *layer = nullptr;
2389 QgsFeature feature;
2390
2391 // TODO this expression function is NOT thread safe
2393 if ( values.isEmpty() )
2394 {
2395 feature = context->feature();
2396 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2397 }
2398 else if ( values.size() == 1 )
2399 {
2400 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2401 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2402 }
2403 else if ( values.size() == 2 )
2404 {
2405 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2406 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2407 }
2408 else
2409 {
2410 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2411 return QVariant();
2412 }
2414
2415 if ( !layer )
2416 {
2417 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2418 return QVariant();
2419 }
2420
2421 if ( !feature.isValid() )
2422 {
2423 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2424 return QVariant();
2425 }
2426
2427 const QgsFields fields = feature.fields();
2428 QVariantMap result;
2429 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2430 {
2431 const QString fieldName { fields.at( fieldIndex ).name() };
2432 const QVariant attributeVal = feature.attribute( fieldIndex );
2433 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2434 if ( context && context->hasCachedValue( cacheValueKey ) )
2435 {
2436 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2437 }
2438 else
2439 {
2440 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2442 QVariant cache;
2443 if ( context )
2444 {
2445 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2446
2447 if ( !context->hasCachedValue( cacheKey ) )
2448 {
2449 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2450 context->setCachedValue( cacheKey, cache );
2451 }
2452 else
2453 {
2454 cache = context->cachedValue( cacheKey );
2455 }
2456 }
2457 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2458
2459 result.insert( fields.at( fieldIndex ).name(), value );
2460
2461 if ( context )
2462 {
2463 context->setCachedValue( cacheValueKey, value );
2464 }
2465 }
2466 }
2467 return result;
2468}
2469
2470static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2471{
2472 QgsVectorLayer *layer = nullptr;
2473 QgsFeature feature;
2474 bool evaluate = true;
2475
2476 // TODO this expression function is NOT thread safe
2478 if ( values.isEmpty() )
2479 {
2480 feature = context->feature();
2481 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2482 }
2483 else if ( values.size() == 1 )
2484 {
2485 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2486 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2487 }
2488 else if ( values.size() == 2 )
2489 {
2490 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2491 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2492 }
2493 else if ( values.size() == 3 )
2494 {
2495 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2496 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2497 evaluate = values.value( 2 ).toBool();
2498 }
2499 else
2500 {
2501 if ( isMaptip )
2502 {
2503 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2504 }
2505 else
2506 {
2507 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2508 }
2509 return QVariant();
2510 }
2511
2512 if ( !layer )
2513 {
2514 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2515 return QVariant();
2516 }
2518
2519 if ( !feature.isValid() )
2520 {
2521 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2522 return QVariant();
2523 }
2524
2525 if ( !evaluate )
2526 {
2527 if ( isMaptip )
2528 {
2529 return layer->mapTipTemplate();
2530 }
2531 else
2532 {
2533 return layer->displayExpression();
2534 }
2535 }
2536
2537 QgsExpressionContext subContext( *context );
2538 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2539 subContext.setFeature( feature );
2540
2541 if ( isMaptip )
2542 {
2543 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2544 }
2545 else
2546 {
2547 QgsExpression exp( layer->displayExpression() );
2548 exp.prepare( &subContext );
2549 return exp.evaluate( &subContext ).toString();
2550 }
2551}
2552
2553static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2554{
2555 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2556}
2557
2558static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2559{
2560 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2561}
2562
2563static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2564{
2565 QgsFeature feature;
2566 QVariant layer;
2567 if ( values.isEmpty() )
2568 {
2569 feature = context->feature();
2570 layer = context->variable( u"layer"_s );
2571 }
2572 else if ( values.size() == 1 )
2573 {
2574 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2575 layer = context->variable( u"layer"_s );
2576 }
2577 else if ( values.size() == 2 )
2578 {
2579 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2580 layer = values.at( 0 );
2581 }
2582 else
2583 {
2584 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2585 return QVariant();
2586 }
2587
2588 bool foundLayer = false;
2589 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2590 layer,
2591 context,
2592 parent,
2593 [feature]( QgsMapLayer *mapLayer ) -> QVariant {
2594 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2595 if ( !layer || !feature.isValid() )
2596 {
2597 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2598 }
2599
2600 return layer->selectedFeatureIds().contains( feature.id() );
2601 },
2602 foundLayer
2603 );
2604 if ( !foundLayer )
2605 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2606 else
2607 return res;
2608}
2609
2610static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2611{
2612 QVariant layer;
2613
2614 if ( values.isEmpty() )
2615 layer = context->variable( u"layer"_s );
2616 else if ( values.count() == 1 )
2617 layer = values.at( 0 );
2618 else
2619 {
2620 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2621 return QVariant();
2622 }
2623
2624 bool foundLayer = false;
2625 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2626 layer,
2627 context,
2628 parent,
2629 []( QgsMapLayer *mapLayer ) -> QVariant {
2630 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2631 if ( !layer )
2632 {
2633 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2634 }
2635
2636 return layer->selectedFeatureCount();
2637 },
2638 foundLayer
2639 );
2640 if ( !foundLayer )
2641 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2642 else
2643 return res;
2644}
2645
2646static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2647{
2648 static QMap<QString, qlonglong> counterCache;
2649 QVariant functionResult;
2650
2651 auto fetchAndIncrementFunc = [values, parent, &functionResult]( QgsMapLayer *mapLayer, const QString &databaseArgument ) {
2652 QString database;
2653
2654 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2655
2656 if ( layer )
2657 {
2658 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2659 database = decodedUri.value( u"path"_s ).toString();
2660 if ( database.isEmpty() )
2661 {
2662 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2663 }
2664 }
2665 else
2666 {
2667 database = databaseArgument;
2668 }
2669
2670 const QString table = values.at( 1 ).toString();
2671 const QString idColumn = values.at( 2 ).toString();
2672 const QString filterAttribute = values.at( 3 ).toString();
2673 const QVariant filterValue = values.at( 4 ).toString();
2674 const QVariantMap defaultValues = values.at( 5 ).toMap();
2675
2676 // read from database
2678 sqlite3_statement_unique_ptr sqliteStatement;
2679
2680 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2681 {
2682 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2683 functionResult = QVariant();
2684 return;
2685 }
2686
2687 QString errorMessage;
2688 QString currentValSql;
2689
2690 qlonglong nextId = 0;
2691 bool cachedMode = false;
2692 bool valueRetrieved = false;
2693
2694 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2695
2696 // Running in transaction mode, check for cached value first
2697 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2698 {
2699 cachedMode = true;
2700
2701 auto cachedCounter = counterCache.find( cacheString );
2702
2703 if ( cachedCounter != counterCache.end() )
2704 {
2705 qlonglong &cachedValue = cachedCounter.value();
2706 nextId = cachedValue;
2707 nextId += 1;
2708 cachedValue = nextId;
2709 valueRetrieved = true;
2710 }
2711 }
2712
2713 // Either not in cached mode or no cached value found, obtain from DB
2714 if ( !cachedMode || !valueRetrieved )
2715 {
2716 int result = SQLITE_ERROR;
2717
2718 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2719 if ( !filterAttribute.isNull() )
2720 {
2721 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2722 }
2723
2724 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2725
2726 if ( result == SQLITE_OK )
2727 {
2728 nextId = 0;
2729 if ( sqliteStatement.step() == SQLITE_ROW )
2730 {
2731 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2732 }
2733
2734 // If in cached mode: add value to cache and connect to transaction
2735 if ( cachedMode && result == SQLITE_OK )
2736 {
2737 counterCache.insert( cacheString, nextId );
2738
2739 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() { counterCache.remove( cacheString ); } );
2740 }
2741 valueRetrieved = true;
2742 }
2743 }
2744
2745 if ( valueRetrieved )
2746 {
2747 QString upsertSql;
2748 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2749 QStringList cols;
2750 QStringList vals;
2751 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2752 vals << QgsSqliteUtils::quotedValue( nextId );
2753
2754 if ( !filterAttribute.isNull() )
2755 {
2756 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2757 vals << QgsSqliteUtils::quotedValue( filterValue );
2758 }
2759
2760 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2761 {
2762 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2763 vals << iter.value().toString();
2764 }
2765
2766 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2767 upsertSql += " VALUES "_L1;
2768 upsertSql += '(' + vals.join( ',' ) + ')';
2769
2770 int result = SQLITE_ERROR;
2771 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2772 {
2773 QgsTransaction *transaction = layer->dataProvider()->transaction();
2774 if ( transaction->executeSql( upsertSql, errorMessage ) )
2775 {
2776 result = SQLITE_OK;
2777 }
2778 }
2779 else
2780 {
2781 result = sqliteDb.exec( upsertSql, errorMessage );
2782 }
2783 if ( result == SQLITE_OK )
2784 {
2785 functionResult = QVariant( nextId );
2786 return;
2787 }
2788 else
2789 {
2790 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2791 functionResult = QVariant();
2792 return;
2793 }
2794 }
2795
2796 functionResult = QVariant();
2797 };
2798
2799 bool foundLayer = false;
2800 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer *layer ) { fetchAndIncrementFunc( layer, QString() ); }, foundLayer );
2801 if ( !foundLayer )
2802 {
2803 const QString databasePath = values.at( 0 ).toString();
2804 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath] { fetchAndIncrementFunc( nullptr, databasePath ); } );
2805 }
2806
2807 return functionResult;
2808}
2809
2810static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2811{
2812 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2813 if ( !crs.isValid() )
2814 return QVariant();
2815 return crs.authid();
2816}
2817
2818static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2819{
2820 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2821 QgsCoordinateReferenceSystem crs( definition );
2822
2823 if ( !crs.isValid() )
2824 {
2825 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2826 }
2827
2828 return QVariant::fromValue( crs );
2829}
2830
2831static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2832{
2833 QString concat;
2834 for ( const QVariant &value : values )
2835 {
2836 if ( !QgsVariantUtils::isNull( value ) )
2837 concat += QgsExpressionUtils::getStringValue( value, parent );
2838 }
2839 return concat;
2840}
2841
2842static QVariant fcnConcatWs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2843{
2844 if ( values.length() < 2 )
2845 {
2846 parent->setEvalErrorString( QObject::tr( "Function concat_ws requires at least 2 arguments" ) );
2847 return QVariant();
2848 }
2849
2850 const QString separator = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2851
2852 QStringList stringValues;
2853 stringValues.reserve( values.size() - 1 );
2854 for ( int i = 1; i < values.size(); ++i )
2855 {
2856 const QVariant value = values.at( i );
2857 if ( !QgsVariantUtils::isNull( value ) )
2858 {
2859 stringValues.append( QgsExpressionUtils::getStringValue( value, parent ) );
2860 }
2861 }
2862
2863 return stringValues.join( separator );
2864}
2865
2866static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2867{
2868 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2869 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2870}
2871
2872static QVariant fcnUnaccent( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction *node )
2873{
2874 Q_UNUSED( context )
2875 Q_UNUSED( node )
2876
2877 if ( values.isEmpty() || values[0].isNull() )
2878 return QVariant();
2879
2880 return QgsStringUtils::unaccent( values[0].toString() );
2881}
2882
2883
2884static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2885{
2886 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2887 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2888 return string.right( pos );
2889}
2890
2891static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2892{
2893 if ( values.length() < 2 || values.length() > 3 )
2894 return QVariant();
2895
2896 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2897 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2898
2899 bool overlapping = false;
2900 if ( values.length() == 3 )
2901 {
2902 overlapping = values.at( 2 ).toBool();
2903 }
2904
2905 if ( substring.isEmpty() )
2906 return QVariant( 0 );
2907
2908 int count = 0;
2909 if ( overlapping )
2910 {
2911 count = input.count( substring );
2912 }
2913 else
2914 {
2915 int pos = 0;
2916 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2917 {
2918 count++;
2919 pos += substring.length();
2920 }
2921 }
2922
2923 return QVariant( count );
2924}
2925
2926static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2927{
2928 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2929 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2930 return string.left( pos );
2931}
2932
2933static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2934{
2935 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2936 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2937 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2938 return string.leftJustified( length, fill.at( 0 ), true );
2939}
2940
2941static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2942{
2943 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2944 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2945 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2946 return string.rightJustified( length, fill.at( 0 ), true );
2947}
2948
2949static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2950{
2951 if ( values.size() < 1 )
2952 {
2953 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2954 return QVariant();
2955 }
2956
2957 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2958 for ( int n = 1; n < values.length(); n++ )
2959 {
2960 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2961 }
2962 return string;
2963}
2964
2965
2966static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2967{
2968 return QVariant( QDateTime::currentDateTime() );
2969}
2970
2971static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2972{
2973 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2974 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2975 if ( format.isEmpty() && !language.isEmpty() )
2976 {
2977 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2978 return QVariant( QDate() );
2979 }
2980
2981 if ( format.isEmpty() && language.isEmpty() )
2982 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2983
2984 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2985 QLocale locale = QLocale();
2986 if ( !language.isEmpty() )
2987 {
2988 locale = QLocale( language );
2989 }
2990
2991 QDate date = locale.toDate( datestring, format );
2992 if ( !date.isValid() )
2993 {
2994 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2995 date = QDate();
2996 }
2997 return QVariant( date );
2998}
2999
3000static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3001{
3002 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3003 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3004 if ( format.isEmpty() && !language.isEmpty() )
3005 {
3006 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
3007 return QVariant( QTime() );
3008 }
3009
3010 if ( format.isEmpty() && language.isEmpty() )
3011 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
3012
3013 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3014 QLocale locale = QLocale();
3015 if ( !language.isEmpty() )
3016 {
3017 locale = QLocale( language );
3018 }
3019
3020 QTime time = locale.toTime( timestring, format );
3021 if ( !time.isValid() )
3022 {
3023 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
3024 time = QTime();
3025 }
3026 return QVariant( time );
3027}
3028
3029static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3030{
3031 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
3032}
3033
3034/*
3035 * DMS functions
3036 */
3037
3038static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3039{
3040 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3041 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3042 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3043
3044 QString formatString;
3045 if ( values.count() > 3 )
3046 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
3047
3049 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
3050 {
3052 }
3053 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
3054 {
3056 }
3057 else if ( !formatString.isEmpty() )
3058 {
3059 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
3060 return QVariant();
3061 }
3062
3063 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
3064 {
3065 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
3066 }
3067 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
3068 {
3069 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
3070 }
3071 else
3072 {
3073 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
3074 return QVariant();
3075 }
3076}
3077
3078static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3079{
3081 return floatToDegreeFormat( format, values, context, parent, node );
3082}
3083
3084static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3085{
3086 double value = 0.0;
3087 bool ok = false;
3088 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
3089
3090 return ok ? QVariant( value ) : QVariant();
3091}
3092
3093static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3094{
3096 return floatToDegreeFormat( format, values, context, parent, node );
3097}
3098
3099static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3100{
3101 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3102 return static_cast< int >( decimalDegrees );
3103}
3104
3105static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3106{
3107 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3108 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3109 return static_cast< int >( remainder * 60 );
3110}
3111
3112static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3113{
3114 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3115 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3116 const double remainderInMinutes = remainder * 60;
3117 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
3118 // do not truncate to int, this function returns decimal seconds!
3119 return remainderSecondsFraction * 60;
3120}
3121
3122static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3123{
3124 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3125 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3126 qint64 seconds = d2.secsTo( d1 );
3127 return QVariant::fromValue( QgsInterval( seconds ) );
3128}
3129
3130static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3131{
3132 if ( !values.at( 0 ).canConvert<QDate>() )
3133 return QVariant();
3134
3135 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
3136 if ( !date.isValid() )
3137 return QVariant();
3138
3139 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
3140 // (to match PostgreSQL behavior)
3141 return date.dayOfWeek() % 7;
3142}
3143
3144static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3145{
3146 QVariant value = values.at( 0 );
3147 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3148 if ( inter.isValid() )
3149 {
3150 return QVariant( inter.days() );
3151 }
3152 else
3153 {
3154 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3155 return QVariant( d1.date().day() );
3156 }
3157}
3158
3159static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3160{
3161 QVariant value = values.at( 0 );
3162 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3163 if ( inter.isValid() )
3164 {
3165 return QVariant( inter.years() );
3166 }
3167 else
3168 {
3169 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3170 return QVariant( d1.date().year() );
3171 }
3172}
3173
3174static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3175{
3176 QVariant value = values.at( 0 );
3177 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3178 if ( inter.isValid() )
3179 {
3180 return QVariant( inter.months() );
3181 }
3182 else
3183 {
3184 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3185 return QVariant( d1.date().month() );
3186 }
3187}
3188
3189static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3190{
3191 QVariant value = values.at( 0 );
3192 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3193 if ( inter.isValid() )
3194 {
3195 return QVariant( inter.weeks() );
3196 }
3197 else
3198 {
3199 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3200 return QVariant( d1.date().weekNumber() );
3201 }
3202}
3203
3204static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3205{
3206 QVariant value = values.at( 0 );
3207 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3208 if ( inter.isValid() )
3209 {
3210 return QVariant( inter.hours() );
3211 }
3212 else
3213 {
3214 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3215 return QVariant( t1.hour() );
3216 }
3217}
3218
3219static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3220{
3221 QVariant value = values.at( 0 );
3222 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3223 if ( inter.isValid() )
3224 {
3225 return QVariant( inter.minutes() );
3226 }
3227 else
3228 {
3229 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3230 return QVariant( t1.minute() );
3231 }
3232}
3233
3234static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3235{
3236 QVariant value = values.at( 0 );
3237 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3238 if ( inter.isValid() )
3239 {
3240 return QVariant( inter.seconds() );
3241 }
3242 else
3243 {
3244 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3245 return QVariant( t1.second() );
3246 }
3247}
3248
3249static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3250{
3251 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3252 if ( dt.isValid() )
3253 {
3254 return QVariant( dt.toMSecsSinceEpoch() );
3255 }
3256 else
3257 {
3258 return QVariant();
3259 }
3260}
3261
3262static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3263{
3264 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3265 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3266 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3267}
3268
3269static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3270{
3271 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3272 if ( parent->hasEvalError() )
3273 {
3274 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3275 return QVariant();
3276 }
3277 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3278 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3279}
3280
3281static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3283 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3284 if ( parent->hasEvalError() )
3285 {
3286 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3287 return QVariant();
3288 }
3289 bool ok;
3290 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3291}
3292
3293double qDateTimeToDecimalYear( const QDateTime &dateTime )
3294{
3295 if ( !dateTime.isValid() )
3296 {
3297 return 0.0;
3298 }
3299
3300 const int year = dateTime.date().year();
3301 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3302 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3303 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3304 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3305 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3306}
3307
3308static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3309{
3310 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3311 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3312 if ( parent->hasEvalError() )
3313 {
3314 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3315 return QVariant();
3316 }
3317 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3318 if ( parent->hasEvalError() )
3319 {
3320 return QVariant();
3321 }
3322 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3323 if ( parent->hasEvalError() )
3324 {
3325 return QVariant();
3326 }
3327 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3328 if ( parent->hasEvalError() )
3329 {
3330 return QVariant();
3331 }
3332 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3333
3334 const QgsMagneticModel model( name, filePath );
3335 try
3336 {
3337 double declination = 0;
3338 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3339 {
3340 return declination;
3341 }
3342 else
3343 {
3344 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3345 }
3346 }
3347 catch ( QgsNotSupportedException &e )
3348 {
3349 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3350 }
3351 return QVariant();
3352}
3353
3354static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3355{
3356 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3357 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3358 if ( parent->hasEvalError() )
3359 {
3360 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3361 return QVariant();
3362 }
3363 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3364 if ( parent->hasEvalError() )
3365 {
3366 return QVariant();
3367 }
3368 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3369 if ( parent->hasEvalError() )
3370 {
3371 return QVariant();
3372 }
3373 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3374 if ( parent->hasEvalError() )
3375 {
3376 return QVariant();
3377 }
3378 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3379
3380 const QgsMagneticModel model( name, filePath );
3381 try
3382 {
3383 double inclination = 0;
3384 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3385 {
3386 return inclination;
3387 }
3388 else
3389 {
3390 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3391 }
3392 }
3393 catch ( QgsNotSupportedException &e )
3394 {
3395 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3396 }
3397 return QVariant();
3398}
3399
3400static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3401{
3402 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3403 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3404 if ( parent->hasEvalError() )
3405 {
3406 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3407 return QVariant();
3408 }
3409 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3410 if ( parent->hasEvalError() )
3411 {
3412 return QVariant();
3413 }
3414 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3415 if ( parent->hasEvalError() )
3416 {
3417 return QVariant();
3418 }
3419 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3420 if ( parent->hasEvalError() )
3421 {
3422 return QVariant();
3423 }
3424 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3425
3426 const QgsMagneticModel model( name, filePath );
3427 try
3428 {
3429 double declination = 0;
3430 double Bx = 0;
3431 double By = 0;
3432 double Bz = 0;
3433 double Bxt = 0;
3434 double Byt = 0;
3435 double Bzt = 0;
3436
3437 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3438 {
3439 double H = 0;
3440 double F = 0;
3441 double D = 0;
3442 double I = 0;
3443 double Ht = 0;
3444 double Ft = 0;
3445 double Dt = 0;
3446 double It = 0;
3447 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3448 {
3449 return Dt;
3450 }
3451 else
3452 {
3453 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3454 }
3455 return declination;
3456 }
3457 else
3458 {
3459 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3460 }
3461 }
3462 catch ( QgsNotSupportedException &e )
3463 {
3464 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3465 }
3466 return QVariant();
3467}
3468
3469static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3470{
3471 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3472 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3473 if ( parent->hasEvalError() )
3474 {
3475 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3476 return QVariant();
3477 }
3478 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3479 if ( parent->hasEvalError() )
3480 {
3481 return QVariant();
3482 }
3483 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3484 if ( parent->hasEvalError() )
3485 {
3486 return QVariant();
3487 }
3488 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3489 if ( parent->hasEvalError() )
3490 {
3491 return QVariant();
3492 }
3493 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3494
3495 const QgsMagneticModel model( name, filePath );
3496 try
3497 {
3498 double declination = 0;
3499 double Bx = 0;
3500 double By = 0;
3501 double Bz = 0;
3502 double Bxt = 0;
3503 double Byt = 0;
3504 double Bzt = 0;
3505
3506 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3507 {
3508 double H = 0;
3509 double F = 0;
3510 double D = 0;
3511 double I = 0;
3512 double Ht = 0;
3513 double Ft = 0;
3514 double Dt = 0;
3515 double It = 0;
3516 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3517 {
3518 return It;
3519 }
3520 else
3521 {
3522 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3523 }
3524 return declination;
3525 }
3526 else
3528 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3529 }
3530 }
3531 catch ( QgsNotSupportedException &e )
3532 {
3533 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3534 }
3535 return QVariant();
3536}
3537
3538#define ENSURE_GEOM_TYPE( f, g, geomtype ) \
3539 if ( !( f ).hasGeometry() ) \
3540 return QVariant(); \
3541 QgsGeometry g = ( f ).geometry(); \
3542 if ( ( g ).type() != ( geomtype ) ) \
3543 return QVariant();
3544
3545static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3546{
3547 FEAT_FROM_CONTEXT( context, f )
3549 if ( g.isMultipart() )
3550 {
3551 return g.asMultiPoint().at( 0 ).x();
3552 }
3553 else
3554 {
3555 return g.asPoint().x();
3556 }
3557}
3558
3559static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3560{
3561 FEAT_FROM_CONTEXT( context, f )
3563 if ( g.isMultipart() )
3564 {
3565 return g.asMultiPoint().at( 0 ).y();
3566 }
3567 else
3568 {
3569 return g.asPoint().y();
3570 }
3571}
3572
3573static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3574{
3575 FEAT_FROM_CONTEXT( context, f )
3577
3578 if ( g.isEmpty() )
3579 return QVariant();
3580
3581 const QgsAbstractGeometry *abGeom = g.constGet();
3582
3583 if ( g.isEmpty() || !abGeom->is3D() )
3584 return QVariant();
3585
3586 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3587 {
3588 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3589 if ( point )
3590 return point->z();
3591 }
3592 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3593 {
3594 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3595 {
3596 if ( collection->numGeometries() > 0 )
3597 {
3598 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3599 return point->z();
3600 }
3601 }
3602 }
3603
3604 return QVariant();
3605}
3606
3607static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3608{
3609 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3610 if ( geom.isNull() )
3611 return QVariant();
3612
3613 bool isValid = geom.isGeosValid();
3614
3615 return QVariant( isValid );
3616}
3617
3618static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3619{
3620 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3621 if ( geom.isNull() )
3622 return QVariant();
3623
3624 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3625#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
3627#else
3629#endif
3630 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3632 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3634
3635 const bool keepCollapsed = values.value( 2 ).toBool();
3636
3637 QgsGeometry valid;
3638 try
3639 {
3640 valid = geom.makeValid( method, keepCollapsed );
3641 }
3642 catch ( QgsNotSupportedException & )
3643 {
3644 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3645 return QVariant();
3646 }
3647
3648 return QVariant::fromValue( valid );
3649}
3650
3651static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3652{
3653 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3654 if ( geom.isNull() )
3655 return QVariant();
3656
3657 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3658 QVariantList array;
3659 for ( int i = 0; i < multiGeom.size(); ++i )
3660 {
3661 array += QVariant::fromValue( multiGeom.at( i ) );
3662 }
3663
3664 return array;
3665}
3666
3667static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3668{
3669 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3670 if ( geom.isNull() )
3671 return QVariant();
3672
3673 //if single point, return the point's x coordinate
3674 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3675 {
3676 return geom.asPoint().x();
3677 }
3678
3679 //otherwise return centroid x
3680 QgsGeometry centroid = geom.centroid();
3681 QVariant result( centroid.asPoint().x() );
3682 return result;
3683}
3684
3685static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3686{
3687 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3688 if ( geom.isNull() )
3689 return QVariant();
3690
3691 //if single point, return the point's y coordinate
3692 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3693 {
3694 return geom.asPoint().y();
3695 }
3696
3697 //otherwise return centroid y
3698 QgsGeometry centroid = geom.centroid();
3699 QVariant result( centroid.asPoint().y() );
3700 return result;
3701}
3702
3703static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3704{
3705 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3706 if ( geom.isNull() )
3707 return QVariant(); //or 0?
3708
3709 if ( !geom.constGet()->is3D() )
3710 return QVariant();
3711
3712 //if single point, return the point's z coordinate
3713 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3714 {
3716 if ( point )
3717 return point->z();
3718 }
3719 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3720 {
3722 {
3723 if ( collection->numGeometries() == 1 )
3724 {
3725 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3726 return point->z();
3727 }
3728 }
3729 }
3730
3731 return QVariant();
3732}
3733
3734static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3735{
3736 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3737 if ( geom.isNull() )
3738 return QVariant(); //or 0?
3739
3740 if ( !geom.constGet()->isMeasure() )
3741 return QVariant();
3742
3743 //if single point, return the point's m value
3744 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3745 {
3747 if ( point )
3748 return point->m();
3749 }
3750 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3751 {
3753 {
3754 if ( collection->numGeometries() == 1 )
3755 {
3756 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3757 return point->m();
3758 }
3759 }
3760 }
3761
3762 return QVariant();
3763}
3764
3765static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3766{
3767 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3768
3769 if ( geom.isNull() )
3770 return QVariant();
3771
3772 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3773
3774 if ( idx < 0 )
3775 {
3776 //negative idx
3777 int count = geom.constGet()->nCoordinates();
3778 idx = count + idx;
3779 }
3780 else
3781 {
3782 //positive idx is 1 based
3783 idx -= 1;
3784 }
3785
3786 QgsVertexId vId;
3787 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3788 {
3789 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3790 return QVariant();
3791 }
3792
3793 QgsPoint point = geom.constGet()->vertexAt( vId );
3794 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3795}
3796
3797static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3798{
3799 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3800
3801 if ( geom.isNull() )
3802 return QVariant();
3803
3804 QgsVertexId vId;
3805 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3806 {
3807 return QVariant();
3808 }
3809
3810 QgsPoint point = geom.constGet()->vertexAt( vId );
3811 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3812}
3813
3814static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3815{
3816 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3817
3818 if ( geom.isNull() )
3819 return QVariant();
3820
3821 QgsVertexId vId;
3822 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3823 {
3824 return QVariant();
3825 }
3826
3827 QgsPoint point = geom.constGet()->vertexAt( vId );
3828 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3829}
3830
3831static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3832{
3833 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3834
3835 if ( geom.isNull() )
3836 return QVariant();
3837
3838 bool ignoreClosing = false;
3839 if ( values.length() > 1 )
3840 {
3841 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3842 }
3843
3844 QgsMultiPoint *mp = new QgsMultiPoint();
3845
3846 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3847 for ( const QgsRingSequence &part : sequence )
3848 {
3849 for ( const QgsPointSequence &ring : part )
3850 {
3851 bool skipLast = false;
3852 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3853 {
3854 skipLast = true;
3855 }
3856
3857 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++i )
3858 {
3859 mp->addGeometry( ring.at( i ).clone() );
3860 }
3861 }
3862 }
3863
3864 return QVariant::fromValue( QgsGeometry( mp ) );
3865}
3866
3867static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3868{
3869 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3870
3871 if ( geom.isNull() )
3872 return QVariant();
3873
3874 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3875
3876 //OK, now we have a complete list of segmentized lines from the geometry
3878 for ( QgsLineString *line : linesToProcess )
3879 {
3880 for ( int i = 0; i < line->numPoints() - 1; ++i )
3881 {
3883 segment->setPoints( QgsPointSequence() << line->pointN( i ) << line->pointN( i + 1 ) );
3884 ml->addGeometry( segment );
3885 }
3886 delete line;
3887 }
3888
3889 return QVariant::fromValue( QgsGeometry( ml ) );
3890}
3891
3892static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3893{
3894 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3895
3896 if ( geom.isNull() )
3897 return QVariant();
3898
3900 if ( !curvePolygon && geom.isMultipart() )
3901 {
3903 {
3904 if ( collection->numGeometries() == 1 )
3905 {
3906 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3907 }
3908 }
3909 }
3910
3911 if ( !curvePolygon )
3912 return QVariant();
3913
3914 //idx is 1 based
3915 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3916
3917 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3918 return QVariant();
3919
3920 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3921 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3922 return result;
3923}
3924
3925static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3926{
3927 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3928
3929 if ( geom.isNull() )
3930 return QVariant();
3931
3933 if ( !collection )
3934 return QVariant();
3935
3936 //idx is 1 based
3937 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3938
3939 if ( idx < 0 || idx >= collection->numGeometries() )
3940 return QVariant();
3941
3942 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3943 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3944 return result;
3945}
3946
3947static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3948{
3949 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3950
3951 if ( geom.isNull() )
3952 return QVariant();
3953
3954 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3955 if ( !boundary )
3956 return QVariant();
3957
3958 return QVariant::fromValue( QgsGeometry( boundary ) );
3959}
3960
3961static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3962{
3963 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3964
3965 if ( geom.isNull() )
3966 return QVariant();
3967
3968 QgsGeometry merged = geom.mergeLines();
3969 if ( merged.isNull() )
3970 return QVariant();
3971
3972 return QVariant::fromValue( merged );
3973}
3974
3975static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3976{
3977 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3978 if ( geom.isNull() )
3979 return QVariant();
3980
3981 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3982 if ( geom2.isNull() )
3983 return QVariant();
3984
3985 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3986 if ( sharedPaths.isNull() )
3987 return QVariant();
3988
3989 return QVariant::fromValue( sharedPaths );
3990}
3991
3992
3993static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3994{
3995 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3996
3997 if ( geom.isNull() )
3998 return QVariant();
3999
4000 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4001
4002 QgsGeometry simplified = geom.simplify( tolerance );
4003 if ( simplified.isNull() )
4004 return QVariant();
4005
4006 return simplified;
4007}
4008
4009static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4010{
4011 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4012
4013 if ( geom.isNull() )
4014 return QVariant();
4015
4016 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4017
4019
4020 QgsGeometry simplified = simplifier.simplify( geom );
4021 if ( simplified.isNull() )
4022 return QVariant();
4023
4024 return simplified;
4025}
4026
4027static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4028{
4029 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4030
4031 if ( geom.isNull() )
4032 return QVariant();
4033
4034 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
4035 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
4036 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4037 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
4038
4039 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
4040 if ( smoothed.isNull() )
4041 return QVariant();
4042
4043 return smoothed;
4044}
4045
4046static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4047{
4048 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4049
4050 if ( geom.isNull() )
4051 return QVariant();
4052
4053 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4054 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4055 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4056
4057 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
4058 if ( waved.isNull() )
4059 return QVariant();
4060
4061 return waved;
4062}
4063
4064static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4065{
4066 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4067
4068 if ( geom.isNull() )
4069 return QVariant();
4070
4071 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4072 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4073 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4074 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4075 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4076
4077 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4078 if ( waved.isNull() )
4079 return QVariant();
4080
4081 return waved;
4082}
4083
4084static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4085{
4086 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4087
4088 if ( geom.isNull() )
4089 return QVariant();
4090
4091 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4092 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4093 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4094
4095 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
4096 if ( waved.isNull() )
4097 return QVariant();
4098
4099 return waved;
4100}
4101
4102static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4103{
4104 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4105
4106 if ( geom.isNull() )
4107 return QVariant();
4108
4109 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4110 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4111 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4112 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4113 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4114
4115 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4116 if ( waved.isNull() )
4117 return QVariant();
4118
4119 return waved;
4120}
4121
4122static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4123{
4124 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4125
4126 if ( geom.isNull() )
4127 return QVariant();
4128
4129 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4130 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4131 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4132
4133 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
4134 if ( waved.isNull() )
4135 return QVariant();
4136
4137 return waved;
4138}
4139
4140static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4141{
4142 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4143
4144 if ( geom.isNull() )
4145 return QVariant();
4146
4147 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4148 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4149 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4150 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4151 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4152
4153 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4154 if ( waved.isNull() )
4155 return QVariant();
4156
4157 return waved;
4158}
4159
4160static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4161{
4162 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4163
4164 if ( geom.isNull() )
4165 return QVariant();
4166
4167 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4168 QVector< double > dashPattern;
4169 dashPattern.reserve( pattern.size() );
4170 for ( const QVariant &value : std::as_const( pattern ) )
4171 {
4172 bool ok = false;
4173 double v = value.toDouble( &ok );
4174 if ( ok )
4175 {
4176 dashPattern << v;
4177 }
4178 else
4179 {
4180 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4181 return QgsGeometry();
4182 }
4183 }
4184
4185 if ( dashPattern.size() % 2 != 0 )
4186 {
4187 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4188 return QgsGeometry();
4189 }
4190
4191 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4193 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4195 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4197 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4199 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4201 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4203 else
4204 {
4205 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4206 return QgsGeometry();
4207 }
4208
4209 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4211 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4213 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4215 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4217 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4219 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4221 else
4222 {
4223 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4224 return QgsGeometry();
4225 }
4226
4227 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4229 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4231 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4233 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4235 else
4236 {
4237 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4238 return QgsGeometry();
4239 }
4240
4241 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4242
4243 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4244 if ( result.isNull() )
4245 return QVariant();
4246
4247 return result;
4248}
4249
4250static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4251{
4252 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4253
4254 if ( geom.isNull() )
4255 return QVariant();
4256
4257 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4258 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4259 if ( densified.isNull() )
4260 return QVariant();
4261
4262 return densified;
4263}
4264
4265static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4266{
4267 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4268
4269 if ( geom.isNull() )
4270 return QVariant();
4271
4272 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4273 const QgsGeometry densified = geom.densifyByDistance( distance );
4274 if ( densified.isNull() )
4275 return QVariant();
4276
4277 return densified;
4278}
4279
4280static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4281{
4282 QVariantList list;
4283 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4284 {
4285 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4286 }
4287 else
4288 {
4289 list = values;
4290 }
4291
4292 QVector< QgsGeometry > parts;
4293 parts.reserve( list.size() );
4294 for ( const QVariant &value : std::as_const( list ) )
4295 {
4296 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4297 if ( part.isNull() )
4298 return QgsGeometry();
4299 parts << part;
4300 }
4301
4302 return QgsGeometry::collectGeometry( parts );
4303}
4304
4305static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4306{
4307 if ( values.count() < 2 || values.count() > 4 )
4308 {
4309 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4310 return QVariant();
4311 }
4312
4313 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4314 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4315 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4316 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4317 switch ( values.count() )
4318 {
4319 case 2:
4320 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4321 case 3:
4322 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4323 case 4:
4324 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4325 }
4326 return QVariant(); //avoid warning
4327}
4328
4329static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4330{
4331 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4332 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4333 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4334 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4335}
4336
4337static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4338{
4339 if ( values.empty() )
4340 {
4341 return QVariant();
4342 }
4343
4344 QVector<QgsPoint> points;
4345 points.reserve( values.count() );
4346
4347 auto addPoint = [&points]( const QgsGeometry &geom ) {
4348 if ( geom.isNull() )
4349 return;
4350
4351 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4352 return;
4353
4355 if ( !point )
4356 return;
4357
4358 points << *point;
4359 };
4360
4361 for ( const QVariant &value : values )
4362 {
4363 if ( value.userType() == QMetaType::Type::QVariantList )
4364 {
4365 const QVariantList list = value.toList();
4366 for ( const QVariant &v : list )
4367 {
4368 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4369 }
4370 }
4371 else
4372 {
4373 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4374 }
4375 }
4376
4377 if ( points.count() < 2 )
4378 return QVariant();
4379
4380 return QgsGeometry( new QgsLineString( points ) );
4381}
4382
4383static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4384{
4385 if ( values.count() < 1 )
4386 {
4387 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4388 return QVariant();
4389 }
4390
4391 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4392
4393 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4394 return outerRing; // if it's already a polygon we have nothing to do
4395
4396 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4397 return QVariant();
4398
4399 auto polygon = std::make_unique< QgsPolygon >();
4400
4401 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4402 if ( !exteriorRing && outerRing.isMultipart() )
4403 {
4405 {
4406 if ( collection->numGeometries() == 1 )
4407 {
4408 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4409 }
4410 }
4411 }
4412
4413 if ( !exteriorRing )
4414 return QVariant();
4415
4416 polygon->setExteriorRing( exteriorRing->segmentize() );
4417
4418
4419 for ( int i = 1; i < values.count(); ++i )
4420 {
4421 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4422 if ( ringGeom.isNull() )
4423 continue;
4424
4425 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4426 continue;
4427
4428 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4429 if ( !ring && ringGeom.isMultipart() )
4430 {
4432 {
4433 if ( collection->numGeometries() == 1 )
4434 {
4435 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4436 }
4437 }
4438 }
4439
4440 if ( !ring )
4441 continue;
4442
4443 polygon->addInteriorRing( ring->segmentize() );
4444 }
4445
4446 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4447}
4448
4449static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4450{
4451 auto tr = std::make_unique<QgsTriangle>();
4452 auto lineString = std::make_unique<QgsLineString>();
4453 lineString->clear();
4454
4455 for ( const QVariant &value : values )
4456 {
4457 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4458 if ( geom.isNull() )
4459 return QVariant();
4460
4461 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4462 return QVariant();
4463
4465 if ( !point && geom.isMultipart() )
4466 {
4468 {
4469 if ( collection->numGeometries() == 1 )
4470 {
4471 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4472 }
4473 }
4474 }
4475
4476 if ( !point )
4477 return QVariant();
4478
4479 lineString->addVertex( *point );
4480 }
4481
4482 tr->setExteriorRing( lineString.release() );
4483
4484 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4485}
4486
4487static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4488{
4489 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4490 if ( geom.isNull() )
4491 return QVariant();
4492
4493 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4494 return QVariant();
4495
4496 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4497 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4498
4499 if ( segment < 3 )
4500 {
4501 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4502 return QVariant();
4503 }
4505 if ( !point && geom.isMultipart() )
4506 {
4508 {
4509 if ( collection->numGeometries() == 1 )
4510 {
4511 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4512 }
4513 }
4514 }
4515 if ( !point )
4516 return QVariant();
4517
4518 QgsCircle circ( *point, radius );
4519 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4520}
4521
4522static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4523{
4524 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4525 if ( geom.isNull() )
4526 return QVariant();
4527
4528 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4529 return QVariant();
4530
4531 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4532 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4533 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4534 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4535 if ( segment < 3 )
4536 {
4537 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4538 return QVariant();
4539 }
4541 if ( !point && geom.isMultipart() )
4542 {
4544 {
4545 if ( collection->numGeometries() == 1 )
4546 {
4547 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4548 }
4549 }
4550 }
4551 if ( !point )
4552 return QVariant();
4553
4554 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4555 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4556}
4557
4558static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4559{
4560 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4561 if ( pt1.isNull() )
4562 return QVariant();
4563
4564 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4565 return QVariant();
4566
4567 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4568 if ( pt2.isNull() )
4569 return QVariant();
4570
4571 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4572 return QVariant();
4573
4574 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4575 if ( nbEdges < 3 )
4576 {
4577 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4578 return QVariant();
4579 }
4580
4581 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4583 {
4584 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4585 return QVariant();
4586 }
4587
4589 if ( !center && pt1.isMultipart() )
4590 {
4592 {
4593 if ( collection->numGeometries() == 1 )
4594 {
4595 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4596 }
4597 }
4598 }
4599 if ( !center )
4600 return QVariant();
4601
4603 if ( !corner && pt2.isMultipart() )
4604 {
4606 {
4607 if ( collection->numGeometries() == 1 )
4608 {
4609 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4610 }
4611 }
4612 }
4613 if ( !corner )
4614 return QVariant();
4615
4616 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4617
4618 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4619}
4620
4621static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4622{
4623 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4624 if ( pt1.isNull() )
4625 return QVariant();
4626 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4627 return QVariant();
4628
4629 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4630 if ( pt2.isNull() )
4631 return QVariant();
4632 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4633 return QVariant();
4634
4635 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4636 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4637 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4638
4639 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4640}
4641
4642static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4643{
4644 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4645 if ( pt1.isNull() )
4646 return QVariant();
4647 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4648 return QVariant();
4649
4650 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4651 if ( pt2.isNull() )
4652 return QVariant();
4653 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4654 return QVariant();
4655
4656 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4657 if ( pt3.isNull() )
4658 return QVariant();
4659 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4660 return QVariant();
4661
4662 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4663 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4664 {
4665 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4666 return QVariant();
4667 }
4668 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4669 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4670 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4671 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4672 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4673}
4674
4675static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4676{
4677 if ( geom.isNull() )
4678 return QVariant();
4679
4680 if ( idx < 0 )
4681 {
4682 idx += geom.constGet()->nCoordinates();
4683 }
4684 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4685 {
4686 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4687 return QVariant();
4688 }
4689 return QVariant::fromValue( geom.vertexAt( idx ) );
4690}
4691
4692// function used for the old $ style
4693static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4694{
4695 FEAT_FROM_CONTEXT( context, feature )
4696 const QgsGeometry geom = feature.geometry();
4697 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4698
4699 const QVariant v = pointAt( geom, idx, parent );
4700
4701 if ( !v.isNull() )
4702 return QVariant( v.value<QgsPoint>().x() );
4703 else
4704 return QVariant();
4705}
4706static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4707{
4708 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))
4709 {
4710 return fcnOldXat( values, f, parent, node );
4711 }
4712 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)
4713 {
4714 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4715 }
4716
4717 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4718 if ( geom.isNull() )
4719 {
4720 return QVariant();
4721 }
4722
4723 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4724
4725 const QVariant v = pointAt( geom, vertexNumber, parent );
4726 if ( !v.isNull() )
4727 return QVariant( v.value<QgsPoint>().x() );
4728 else
4729 return QVariant();
4730}
4731
4732// function used for the old $ style
4733static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4734{
4735 FEAT_FROM_CONTEXT( context, feature )
4736 const QgsGeometry geom = feature.geometry();
4737 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4738
4739 const QVariant v = pointAt( geom, idx, parent );
4740
4741 if ( !v.isNull() )
4742 return QVariant( v.value<QgsPoint>().y() );
4743 else
4744 return QVariant();
4745}
4746static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4747{
4748 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))
4749 {
4750 return fcnOldYat( values, f, parent, node );
4751 }
4752 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)
4753 {
4754 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4755 }
4756
4757 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4758 if ( geom.isNull() )
4759 {
4760 return QVariant();
4761 }
4762
4763 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4764
4765 const QVariant v = pointAt( geom, vertexNumber, parent );
4766 if ( !v.isNull() )
4767 return QVariant( v.value<QgsPoint>().y() );
4768 else
4769 return QVariant();
4770}
4771
4772static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4773{
4774 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4775 if ( geom.isNull() )
4776 {
4777 return QVariant();
4778 }
4779
4780 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4781
4782 const QVariant v = pointAt( geom, vertexNumber, parent );
4783 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4784 return QVariant( v.value<QgsPoint>().z() );
4785 else
4786 return QVariant();
4787}
4788
4789static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4790{
4791 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4792 if ( geom.isNull() )
4793 {
4794 return QVariant();
4795 }
4796
4797 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4798
4799 const QVariant v = pointAt( geom, vertexNumber, parent );
4800 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4801 return QVariant( v.value<QgsPoint>().m() );
4802 else
4803 return QVariant();
4804}
4805
4806
4807static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4808{
4809 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4810 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4811 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4812 return result;
4813}
4814
4815static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4816{
4817 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4818 if ( wkb.isNull() )
4819 return QVariant();
4820
4821 QgsGeometry geom;
4822 geom.fromWkb( wkb );
4823 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4824}
4825
4826static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4827{
4828 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4829 QgsOgcUtils::Context ogcContext;
4830 if ( context )
4831 {
4832 QgsWeakMapLayerPointer mapLayerPtr { context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4833 if ( mapLayerPtr )
4834 {
4835 ogcContext.layer = mapLayerPtr.data();
4836 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4837 }
4838 }
4839 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4840 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4841 return result;
4842}
4843
4844static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4845{
4846 FEAT_FROM_CONTEXT( context, f )
4848 QgsDistanceArea *calc = parent->geomCalculator();
4849 if ( calc )
4850 {
4851 try
4852 {
4853 double area = calc->measureArea( f.geometry() );
4854 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4855 return QVariant( area );
4856 }
4857 catch ( QgsCsException & )
4858 {
4859 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4860 return QVariant();
4861 }
4862 }
4863 else
4864 {
4865 return QVariant( f.geometry().area() );
4866 }
4867}
4868
4869static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4870{
4871 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4872
4873 if ( geom.type() != Qgis::GeometryType::Polygon )
4874 return QVariant();
4875
4876 return QVariant( geom.area() );
4877}
4878
4879static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4880{
4881 FEAT_FROM_CONTEXT( context, f )
4883 QgsDistanceArea *calc = parent->geomCalculator();
4884 if ( calc )
4885 {
4886 try
4887 {
4888 double len = calc->measureLength( f.geometry() );
4889 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4890 return QVariant( len );
4891 }
4892 catch ( QgsCsException & )
4893 {
4894 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4895 return QVariant();
4896 }
4897 }
4898 else
4899 {
4900 return QVariant( f.geometry().length() );
4901 }
4902}
4903
4904static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4905{
4906 FEAT_FROM_CONTEXT( context, f )
4908 QgsDistanceArea *calc = parent->geomCalculator();
4909 if ( calc )
4910 {
4911 try
4912 {
4913 double len = calc->measurePerimeter( f.geometry() );
4914 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4915 return QVariant( len );
4916 }
4917 catch ( QgsCsException & )
4918 {
4919 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4920 return QVariant();
4921 }
4922 }
4923 else
4924 {
4925 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4926 }
4927}
4928
4929static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4930{
4931 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4932
4933 if ( geom.type() != Qgis::GeometryType::Polygon )
4934 return QVariant();
4935
4936 //length for polygons = perimeter
4937 return QVariant( geom.length() );
4938}
4939
4940static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4941{
4942 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4943 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4944}
4945
4946static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4947{
4948 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4949 if ( geom.isNull() )
4950 return QVariant();
4951
4952 return QVariant( geom.constGet()->partCount() );
4953}
4954
4955static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4956{
4957 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4958 if ( geom.isNull() )
4959 return QVariant();
4960
4961 return QVariant( geom.isMultipart() );
4962}
4963
4964static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4965{
4966 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4967
4968 if ( geom.isNull() )
4969 return QVariant();
4970
4972 if ( curvePolygon )
4973 return QVariant( curvePolygon->numInteriorRings() );
4974
4976 if ( collection )
4977 {
4978 //find first CurvePolygon in collection
4979 for ( int i = 0; i < collection->numGeometries(); ++i )
4980 {
4981 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4982 if ( !curvePolygon )
4983 continue;
4984
4985 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4986 }
4987 }
4988
4989 return QVariant();
4990}
4991
4992static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4993{
4994 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4995
4996 if ( geom.isNull() )
4997 return QVariant();
4998
5000 if ( curvePolygon )
5001 return QVariant( curvePolygon->ringCount() );
5002
5003 bool foundPoly = false;
5004 int ringCount = 0;
5006 if ( collection )
5007 {
5008 //find CurvePolygons in collection
5009 for ( int i = 0; i < collection->numGeometries(); ++i )
5010 {
5011 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5012 if ( !curvePolygon )
5013 continue;
5014
5015 foundPoly = true;
5016 ringCount += curvePolygon->ringCount();
5017 }
5018 }
5019
5020 if ( !foundPoly )
5021 return QVariant();
5022
5023 return QVariant( ringCount );
5024}
5025
5026static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5027{
5028 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5029 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
5030 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
5031 return result;
5032}
5033
5034static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5035{
5036 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5037 return QVariant::fromValue( geom.boundingBox().width() );
5038}
5039
5040static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5041{
5042 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5043 return QVariant::fromValue( geom.boundingBox().height() );
5044}
5045
5046static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5047{
5048 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5049 if ( geom.isNull() )
5050 return QVariant();
5051
5053}
5054
5055static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5056{
5057 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5058 return QVariant::fromValue( geom.boundingBox().xMinimum() );
5059}
5060
5061static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5062{
5063 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5064 return QVariant::fromValue( geom.boundingBox().xMaximum() );
5065}
5066
5067static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5068{
5069 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5070 return QVariant::fromValue( geom.boundingBox().yMinimum() );
5071}
5072
5073static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5074{
5075 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5076 return QVariant::fromValue( geom.boundingBox().yMaximum() );
5077}
5078
5079static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5080{
5081 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5082
5083 if ( geom.isNull() || geom.isEmpty() )
5084 return QVariant();
5085
5086 if ( !geom.constGet()->is3D() )
5087 return QVariant();
5088
5089 double max = std::numeric_limits< double >::lowest();
5090
5091 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5092 {
5093 double z = ( *it ).z();
5094
5095 if ( max < z )
5096 max = z;
5097 }
5098
5099 if ( max == std::numeric_limits< double >::lowest() )
5100 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5101
5102 return QVariant( max );
5103}
5104
5105static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5106{
5107 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5108
5109 if ( geom.isNull() || geom.isEmpty() )
5110 return QVariant();
5111
5112 if ( !geom.constGet()->is3D() )
5113 return QVariant();
5114
5115 double min = std::numeric_limits< double >::max();
5116
5117 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5118 {
5119 double z = ( *it ).z();
5120
5121 if ( z < min )
5122 min = z;
5123 }
5124
5125 if ( min == std::numeric_limits< double >::max() )
5126 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5127
5128 return QVariant( min );
5129}
5130
5131static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5132{
5133 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5134
5135 if ( geom.isNull() || geom.isEmpty() )
5136 return QVariant();
5137
5138 if ( !geom.constGet()->isMeasure() )
5139 return QVariant();
5140
5141 double min = std::numeric_limits< double >::max();
5142
5143 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5144 {
5145 double m = ( *it ).m();
5146
5147 if ( m < min )
5148 min = m;
5149 }
5150
5151 if ( min == std::numeric_limits< double >::max() )
5152 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5153
5154 return QVariant( min );
5155}
5156
5157static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5158{
5159 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5160
5161 if ( geom.isNull() || geom.isEmpty() )
5162 return QVariant();
5163
5164 if ( !geom.constGet()->isMeasure() )
5165 return QVariant();
5166
5167 double max = std::numeric_limits< double >::lowest();
5168
5169 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5170 {
5171 double m = ( *it ).m();
5172
5173 if ( max < m )
5174 max = m;
5175 }
5176
5177 if ( max == std::numeric_limits< double >::lowest() )
5178 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5179
5180 return QVariant( max );
5181}
5182
5183static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5184{
5185 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5187 if ( !curve )
5188 {
5189 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5190 return QVariant();
5191 }
5192
5193 return QVariant( curve->sinuosity() );
5194}
5195
5196static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5197{
5198 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5199 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5200 if ( !curve )
5201 {
5202 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5203 return QVariant();
5204 }
5205
5206 return QVariant( curve->straightDistance2d() );
5207}
5208
5209static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5210{
5211 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5213
5214 if ( !poly )
5215 {
5216 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5217 return QVariant();
5218 }
5219
5220 return QVariant( poly->roundness() );
5221}
5222
5223
5224static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5225{
5226 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5227 if ( geom.isNull() )
5228 return QVariant();
5229
5230 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5231 flipped->swapXy();
5232 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5233}
5234
5235static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5236{
5237 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5238 if ( fGeom.isNull() )
5239 return QVariant();
5240
5241 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5242 if ( !curve && fGeom.isMultipart() )
5243 {
5245 {
5246 if ( collection->numGeometries() == 1 )
5247 {
5248 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5249 }
5250 }
5251 }
5252
5253 if ( !curve )
5254 return QVariant();
5255
5256 return QVariant::fromValue( curve->isClosed() );
5257}
5258
5259static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5260{
5261 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5262
5263 if ( geom.isNull() )
5264 return QVariant();
5265
5266 QVariant result;
5267 if ( !geom.isMultipart() )
5268 {
5270
5271 if ( !line )
5272 return QVariant();
5273
5274 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5275 closedLine->close();
5276
5277 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5278 }
5279 else
5280 {
5282 if ( !collection )
5283 return QVariant();
5284
5285 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5286
5287 for ( int i = 0; i < collection->numGeometries(); ++i )
5288 {
5289 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5290 {
5291 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5292 closedLine->close();
5293
5294 closed->addGeometry( closedLine.release() );
5295 }
5296 }
5297 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5298 }
5299
5300 return result;
5301}
5302
5303static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5304{
5305 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5306 if ( fGeom.isNull() )
5307 return QVariant();
5308
5309 return QVariant::fromValue( fGeom.isEmpty() );
5310}
5311
5312static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5313{
5314 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5315 return QVariant::fromValue( true );
5316
5317 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5318 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5319}
5320
5321static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5322{
5323 if ( values.length() < 2 || values.length() > 3 )
5324 return QVariant();
5325
5326 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5327 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5328
5329 if ( fGeom.isNull() || sGeom.isNull() )
5330 return QVariant();
5331
5332 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5333
5334 if ( values.length() == 2 )
5335 {
5336 //two geometry arguments, return relation
5337 QString result = engine->relate( sGeom.constGet() );
5338 return QVariant::fromValue( result );
5339 }
5340 else
5341 {
5342 //three arguments, test pattern
5343 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5344 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5345 return QVariant::fromValue( result );
5346 }
5347}
5348
5349static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5350{
5351 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5352 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5353 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5354}
5355static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5356{
5357 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5358 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5359 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5360}
5361static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5362{
5363 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5364 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5365 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5366}
5367static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5368{
5369 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5370 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5371 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5372}
5373static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5374{
5375 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5376 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5377 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5378}
5379static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5380{
5381 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5382 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5383 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5384}
5385static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5386{
5387 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5388 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5389 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5390}
5391static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5392{
5393 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5394 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5395 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5396}
5397
5398static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5399{
5400 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5401 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5402 return fGeom.isExactlyEqual( sGeom ) ? TVL_True : TVL_False;
5403}
5404
5405static QVariant fcnIsEqualsExact( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5406{
5407 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5408 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5409 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5410
5411 bool ok;
5412 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5413 if ( !ok )
5414 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5415
5416 QVariant ret = TVL_False;
5417 try
5418 {
5419 ret = fGeom.isExactlyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5420 }
5421 catch ( QgsNotSupportedException &e )
5422 {
5423 SET_EVAL_ERROR( e.what() );
5424 }
5425 return ret;
5426}
5427
5428static QVariant fcnIsEqualsTopological( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5429{
5430 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5431 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5432 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5433
5434 bool ok;
5435 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
5436 if ( !ok )
5437 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5438
5439 QVariant ret = TVL_False;
5440 try
5441 {
5442 ret = fGeom.isTopologicallyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5443 }
5444 catch ( QgsNotSupportedException &e )
5445 {
5446 SET_EVAL_ERROR( e.what() );
5447 }
5448 return ret;
5449}
5450
5451static QVariant fcnIsEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5452{
5453 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5454 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5455 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5456
5457 bool ok;
5458 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5459 if ( !ok )
5460 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5461
5462 double epsilon = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5463
5464 QVariant ret = TVL_False;
5465 try
5466 {
5467 ret = fGeom.isFuzzyEqual( sGeom, epsilon, backend ) ? TVL_True : TVL_False;
5468 }
5469 catch ( QgsNotSupportedException &e )
5470 {
5471 SET_EVAL_ERROR( e.what() );
5472 }
5473 return ret;
5474}
5475
5476static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5477{
5478 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5479 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5480 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5481 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5482 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5483 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5484
5486 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5487 capStyle = Qgis::EndCapStyle::Flat;
5488 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5489 capStyle = Qgis::EndCapStyle::Square;
5490
5492 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5493 joinStyle = Qgis::JoinStyle::Miter;
5494 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5495 joinStyle = Qgis::JoinStyle::Bevel;
5496
5497 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5498 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5499 return result;
5500}
5501
5502static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5503{
5504 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5505 const QgsGeometry reoriented = fGeom.forceRHR();
5506 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5507}
5508
5509static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5510{
5511 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5512 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5513 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5514}
5515
5516static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5517{
5518 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5519 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5520 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5521}
5522
5523static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5524{
5525 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5527 if ( !pt && fGeom.isMultipart() )
5528 {
5530 {
5531 if ( collection->numGeometries() == 1 )
5532 {
5533 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5534 }
5535 }
5536 }
5537
5538 if ( !pt )
5539 {
5540 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5541 return QVariant();
5542 }
5543
5544 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5545 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5546 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5547 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5548
5549 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5550 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5551 return result;
5552}
5553
5554static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5555{
5556 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5557 if ( fGeom.type() != Qgis::GeometryType::Line )
5558 {
5559 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5560 return QVariant();
5561 }
5562
5563 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5564 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5565 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5566
5567 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5568 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5569 return result;
5570}
5571
5572static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5573{
5574 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5575 if ( fGeom.type() != Qgis::GeometryType::Line )
5576 {
5577 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5578 return QVariant();
5579 }
5580
5581 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5582
5583 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5584 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5585 return result;
5586}
5587
5588static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5589{
5590 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5591 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5592 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5593 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5594 if ( joinInt < 1 || joinInt > 3 )
5595 return QVariant();
5596 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5597
5598 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5599
5600 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5601 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5602 return result;
5603}
5604
5605static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5606{
5607 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5608 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5609 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5610
5611 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5612 if ( joinInt < 1 || joinInt > 3 )
5613 return QVariant();
5614 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5615
5616 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5617
5618 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5619 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5620 return result;
5621}
5622
5623static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5624{
5625 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5626 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5627 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5628
5629 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5630 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5631 return result;
5632}
5633
5634static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5635{
5636 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5637 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5638 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5639 fGeom.translate( dx, dy );
5640 return QVariant::fromValue( fGeom );
5641}
5642
5643static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5644{
5645 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5646 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5647 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent ) : QgsGeometry();
5648 const bool perPart = values.value( 3 ).toBool();
5649
5650 if ( center.isNull() && perPart && fGeom.isMultipart() )
5651 {
5652 // no explicit center, rotating per part
5653 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5654 // the result is equivalent to setting perPart as false anyway)
5655 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5656 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5657 {
5658 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5659 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5660 t.rotate( -rotation );
5661 t.translate( -partCenter.x(), -partCenter.y() );
5662 ( *it )->transform( t );
5663 }
5664 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5665 }
5666 else
5667 {
5668 QgsPointXY pt;
5669 if ( center.isEmpty() )
5670 {
5671 // if center wasn't specified, use bounding box centroid
5672 pt = fGeom.boundingBox().center();
5673 }
5675 {
5676 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5677 return QVariant();
5678 }
5679 else
5680 {
5682 }
5683
5684 fGeom.rotate( rotation, pt );
5685 return QVariant::fromValue( fGeom );
5686 }
5687}
5688
5689static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5690{
5691 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5692 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5693 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5694 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent ) : QgsGeometry();
5695
5696 QgsPointXY pt;
5697 if ( center.isNull() )
5698 {
5699 // if center wasn't specified, use bounding box centroid
5700 pt = fGeom.boundingBox().center();
5701 }
5703 {
5704 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5705 return QVariant();
5706 }
5707 else
5708 {
5709 pt = center.asPoint();
5710 }
5711
5712 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5713 t.scale( xScale, yScale );
5714 t.translate( -pt.x(), -pt.y() );
5715 fGeom.transform( t );
5716 return QVariant::fromValue( fGeom );
5717}
5718
5719static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5720{
5721 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5722 if ( fGeom.isNull() )
5723 {
5724 return QVariant();
5725 }
5726
5727 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5728 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5729
5730 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5731
5732 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5733 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5734
5735 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5736 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5737 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5738 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5739
5740 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5741 {
5742 fGeom.get()->addZValue( 0 );
5743 }
5744 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5745 {
5746 fGeom.get()->addMValue( 0 );
5747 }
5748
5749 QTransform transform;
5750 transform.translate( deltaX, deltaY );
5751 transform.rotate( rotationZ );
5752 transform.scale( scaleX, scaleY );
5753 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5754
5755 return QVariant::fromValue( fGeom );
5756}
5757
5758
5759static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5760{
5761 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5762 QgsGeometry geom = fGeom.centroid();
5763 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5764 return result;
5765}
5766static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5767{
5768 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5769 QgsGeometry geom = fGeom.pointOnSurface();
5770 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5771 return result;
5772}
5773
5774static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5775{
5776 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5777 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5778 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5779 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5780 return result;
5781}
5782
5783static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5784{
5785 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5786 QgsGeometry geom = fGeom.convexHull();
5787 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5788 return result;
5789}
5790
5791#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
5792static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5793{
5794 try
5795 {
5796 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5797 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5798 const bool allowHoles = values.value( 2 ).toBool();
5799 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5800 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5801 return result;
5802 }
5803 catch ( QgsCsException &cse )
5804 {
5805 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5806 return QVariant();
5807 }
5808}
5809#endif
5810
5811static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5812{
5813 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5814 int segments = 36;
5815 if ( values.length() == 2 )
5816 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5817 if ( segments < 0 )
5818 {
5819 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5820 return QVariant();
5821 }
5822
5823 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5824 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5825 return result;
5826}
5827
5828static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5829{
5830 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5832 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5833 return result;
5834}
5835
5836static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5837{
5838 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5839
5840 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5841 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5842 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5843
5844 double area, angle, width, height;
5845 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5846
5847 if ( geom.isNull() )
5848 {
5849 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5850 return QVariant();
5851 }
5852 return angle;
5853}
5854
5855static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5856{
5857 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5858 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5859 QgsGeometry geom = fGeom.difference( sGeom );
5860 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5861 return result;
5862}
5863
5864static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5865{
5866 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5867 return QVariant();
5868
5869 // two variants, one for geometry, one for string
5870
5871 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5872 if ( !fGeom.isNull() )
5873 {
5874 QVariant result;
5875 if ( !fGeom.isMultipart() )
5876 {
5877 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5878 if ( !curve )
5879 return QVariant();
5880
5881 QgsCurve *reversed = curve->reversed();
5882 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5883 }
5884 else
5885 {
5887 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5888 for ( int i = 0; i < collection->numGeometries(); ++i )
5889 {
5890 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5891 {
5892 reversed->addGeometry( curve->reversed() );
5893 }
5894 else
5895 {
5896 reversed->addGeometry( collection->geometryN( i )->clone() );
5897 }
5898 }
5899 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5900 }
5901 return result;
5902 }
5903
5904 //fall back to string variant
5905 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5906 std::reverse( string.begin(), string.end() );
5907 return string;
5908}
5909
5910static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5911{
5912 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5913 if ( fGeom.isNull() )
5914 return QVariant();
5915
5917 if ( !curvePolygon && fGeom.isMultipart() )
5918 {
5920 {
5921 if ( collection->numGeometries() == 1 )
5922 {
5923 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5924 }
5925 }
5926 }
5927
5928 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5929 return QVariant();
5930
5931 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5932 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5933 return result;
5934}
5935
5936static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5937{
5938 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5939 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5940 return QVariant( fGeom.distance( sGeom ) );
5941}
5942
5943static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5944{
5945 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5946 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5947
5948 double res = -1;
5949 if ( values.length() == 3 && values.at( 2 ).isValid() )
5950 {
5951 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5952 densify = std::clamp( densify, 0.0, 1.0 );
5953 res = g1.hausdorffDistanceDensify( g2, densify );
5954 }
5955 else
5956 {
5957 res = g1.hausdorffDistance( g2 );
5958 }
5959
5960 return res > -1 ? QVariant( res ) : QVariant();
5961}
5962
5963static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5964{
5965 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5966 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5967 QgsGeometry geom = fGeom.intersection( sGeom );
5968 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5969 return result;
5970}
5971static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5972{
5973 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5974 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5975 QgsGeometry geom = fGeom.symDifference( sGeom );
5976 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5977 return result;
5978}
5979static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5980{
5981 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5982 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5983 QgsGeometry geom = fGeom.combine( sGeom );
5984 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5985 return result;
5986}
5987
5988static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5989{
5990 if ( values.length() < 1 || values.length() > 2 )
5991 return QVariant();
5992
5993 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5994 int prec = 8;
5995 if ( values.length() == 2 )
5996 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5997 QString wkt = fGeom.asWkt( prec );
5998 return QVariant( wkt );
5999}
6000
6001static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6002{
6003 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6004 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
6005}
6006
6007static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6008{
6009 if ( values.length() != 2 )
6010 {
6011 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
6012 return QVariant();
6013 }
6014
6015 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6016 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6017
6018 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6019 if ( !pt1 && fGeom1.isMultipart() )
6020 {
6022 {
6023 if ( collection->numGeometries() == 1 )
6024 {
6025 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6026 }
6027 }
6028 }
6029
6030 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6031 if ( !pt2 && fGeom2.isMultipart() )
6032 {
6034 {
6035 if ( collection->numGeometries() == 1 )
6036 {
6037 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6038 }
6039 }
6040 }
6041
6042 if ( !pt1 || !pt2 )
6043 {
6044 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
6045 return QVariant();
6046 }
6047
6048 // Code from PostGIS
6049 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
6050 {
6051 if ( pt1->y() < pt2->y() )
6052 return 0.0;
6053 else if ( pt1->y() > pt2->y() )
6054 return M_PI;
6055 else
6056 return 0;
6057 }
6058
6059 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
6060 {
6061 if ( pt1->x() < pt2->x() )
6062 return M_PI_2;
6063 else if ( pt1->x() > pt2->x() )
6064 return M_PI + ( M_PI_2 );
6065 else
6066 return 0;
6067 }
6068
6069 if ( pt1->x() < pt2->x() )
6070 {
6071 if ( pt1->y() < pt2->y() )
6072 {
6073 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
6074 }
6075 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
6076 {
6077 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI_2 );
6078 }
6079 }
6080
6081 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
6082 {
6083 if ( pt1->y() > pt2->y() )
6084 {
6085 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI;
6086 }
6087 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
6088 {
6089 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI_2 ) );
6090 }
6091 }
6092}
6093
6094static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6095{
6096 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6097 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6098 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6099 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
6100
6101 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
6102 {
6103 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
6104 return QVariant();
6105 }
6106
6107 const QgsPointXY point1 = geom1.asPoint();
6108 const QgsPointXY point2 = geom2.asPoint();
6109 if ( point1.isEmpty() || point2.isEmpty() )
6110 {
6111 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
6112 return QVariant();
6113 }
6114
6116 if ( context )
6117 {
6118 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6119
6120 if ( !sourceCrs.isValid() )
6121 {
6122 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
6123 }
6124
6125 if ( ellipsoid.isEmpty() )
6126 {
6127 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
6128 }
6129 }
6130
6131 if ( !sourceCrs.isValid() )
6132 {
6133 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
6134 return QVariant();
6135 }
6136
6137 QgsDistanceArea da;
6138 da.setSourceCrs( sourceCrs, tContext );
6139 if ( !da.setEllipsoid( ellipsoid ) )
6140 {
6141 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
6142 return QVariant();
6143 }
6144
6145 try
6146 {
6147 const double bearing = da.bearing( point1, point2 );
6148 if ( std::isfinite( bearing ) )
6149 {
6150 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
6151 }
6152 }
6153 catch ( QgsCsException &cse )
6154 {
6155 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
6156 return QVariant();
6157 }
6158 return QVariant();
6159}
6160
6161static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6162{
6163 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6164
6166 {
6167 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
6168 return QVariant();
6169 }
6170
6171 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6172 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6173 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
6174
6175 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef() );
6176 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
6177
6178 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
6179}
6180
6181static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6182{
6183 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6184 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6185
6186 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6187 if ( !pt1 && fGeom1.isMultipart() )
6188 {
6190 {
6191 if ( collection->numGeometries() == 1 )
6192 {
6193 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6194 }
6195 }
6196 }
6197 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6198 if ( !pt2 && fGeom2.isMultipart() )
6199 {
6201 {
6202 if ( collection->numGeometries() == 1 )
6203 {
6204 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6205 }
6206 }
6207 }
6208
6209 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) || !pt1 || !pt2 )
6210 {
6211 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6212 return QVariant();
6213 }
6214
6215 return pt1->inclination( *pt2 );
6216}
6217
6218static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6219{
6220 if ( values.length() != 3 )
6221 return QVariant();
6222
6223 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6224 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6225 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6226
6227 QgsGeometry geom = fGeom.extrude( x, y );
6228
6229 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6230 return result;
6231}
6232
6233static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6234{
6235 if ( values.length() < 2 )
6236 return QVariant();
6237
6238 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6239
6240 if ( !fGeom.isMultipart() )
6241 return values.at( 0 );
6242
6243 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6244 QVariant cachedExpression;
6245 if ( ctx )
6246 cachedExpression = ctx->cachedValue( expString );
6247 QgsExpression expression;
6248
6249 if ( cachedExpression.isValid() )
6250 {
6251 expression = cachedExpression.value<QgsExpression>();
6252 }
6253 else
6254 expression = QgsExpression( expString );
6255
6256 bool asc = values.value( 2 ).toBool();
6257
6258 QgsExpressionContext *unconstedContext = nullptr;
6259 QgsFeature f;
6260 if ( ctx )
6261 {
6262 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6263 // so no reason to worry
6264 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6265 f = ctx->feature();
6266 }
6267 else
6268 {
6269 // If there's no context provided, create a fake one
6270 unconstedContext = new QgsExpressionContext();
6271 }
6272
6274 Q_ASSERT( collection ); // Should have failed the multipart check above
6275
6277 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6278 QgsExpressionSorter sorter( orderBy );
6279
6280 QList<QgsFeature> partFeatures;
6281 partFeatures.reserve( collection->partCount() );
6282 for ( int i = 0; i < collection->partCount(); ++i )
6283 {
6284 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6285 partFeatures << f;
6286 }
6287
6288 sorter.sortFeatures( partFeatures, unconstedContext );
6289
6291
6292 Q_ASSERT( orderedGeom );
6293
6294 while ( orderedGeom->partCount() )
6295 orderedGeom->removeGeometry( 0 );
6296
6297 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6298 {
6299 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6300 }
6301
6302 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6303
6304 if ( !ctx )
6305 delete unconstedContext;
6306
6307 return result;
6308}
6309
6310static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6311{
6312 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6313 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6314
6315 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6316
6317 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6318 return result;
6319}
6320
6321static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6322{
6323 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6324 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6325
6326 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6327
6328 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6329 return result;
6330}
6331
6332static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6333{
6334 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6335 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6336
6337 QgsGeometry geom = lineGeom.interpolate( distance );
6338
6339 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6340 return result;
6341}
6342
6343static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6344{
6345 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6346 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6347 const bool use3DDistance = values.at( 2 ).toBool();
6348
6349 double x, y, z, distance;
6350
6352 if ( !line )
6353 {
6354 return QVariant();
6355 }
6356
6357 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6358 {
6359 QgsPoint point( x, y );
6360 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6361 {
6362 point.addZValue( z );
6363 }
6364 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6365 }
6366
6367 return QVariant();
6368}
6369
6370static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6371{
6372 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6373 if ( lineGeom.type() != Qgis::GeometryType::Line )
6374 {
6375 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6376 return QVariant();
6377 }
6378
6379 const QgsCurve *curve = nullptr;
6380 if ( !lineGeom.isMultipart() )
6381 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6382 else
6383 {
6385 {
6386 if ( collection->numGeometries() > 0 )
6387 {
6388 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6389 }
6390 }
6391 }
6392 if ( !curve )
6393 return QVariant();
6394
6395 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6396 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6397
6398 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6399 QgsGeometry result( std::move( substring ) );
6400 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6401}
6402
6403static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6404{
6405 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6406 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6407
6408 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6409}
6410
6411static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6412{
6413 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6414 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6415 if ( vertex < 0 )
6416 {
6417 //negative idx
6418 int count = geom.constGet()->nCoordinates();
6419 vertex = count + vertex;
6420 }
6421
6422 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6423}
6424
6425static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6426{
6427 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6428 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6429 if ( vertex < 0 )
6430 {
6431 //negative idx
6432 int count = geom.constGet()->nCoordinates();
6433 vertex = count + vertex;
6434 }
6435
6436 return geom.distanceToVertex( vertex );
6437}
6438
6439static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6440{
6441 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6442 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6443
6444 double distance = lineGeom.lineLocatePoint( pointGeom );
6445
6446 return distance >= 0 ? distance : QVariant();
6447}
6448
6449static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6450{
6451 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6452 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6453 const bool use3DDistance = values.at( 2 ).toBool();
6454
6455 double x, y, z, distance;
6456
6458 if ( !line )
6459 {
6460 return QVariant();
6461 }
6462
6463 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6464 return found ? distance : QVariant();
6465}
6466
6467static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6468{
6469 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6470 {
6471 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6472 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6473 }
6474
6475 if ( values.length() >= 1 )
6476 {
6477 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6478 return QVariant( qlonglong( std::round( number ) ) );
6479 }
6480
6481 return QVariant();
6482}
6483
6484static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6485{
6486 Q_UNUSED( values )
6487 Q_UNUSED( parent )
6488 return M_PI;
6489}
6490
6491static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6492{
6493 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6494 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6495 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6496 if ( places < 0 )
6497 {
6498 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6499 return QVariant();
6500 }
6501
6502 const bool omitGroupSeparator = values.value( 3 ).toBool();
6503 const bool trimTrailingZeros = values.value( 4 ).toBool();
6504
6505 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6506 if ( !omitGroupSeparator )
6507 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6508 else
6509 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6510
6511 QString res = locale.toString( value, 'f', places );
6512
6513 if ( trimTrailingZeros )
6514 {
6515 const QChar decimal = locale.decimalPoint().at( 0 );
6516 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6517
6518 if ( res.contains( decimal ) )
6519 {
6520 int trimPoint = res.length() - 1;
6521
6522 while ( res.at( trimPoint ) == zeroDigit )
6523 trimPoint--;
6524
6525 if ( res.at( trimPoint ) == decimal )
6526 trimPoint--;
6527
6528 res.truncate( trimPoint + 1 );
6529 }
6530 }
6531
6532 return res;
6533}
6534
6535static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6536{
6537 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6538 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6539 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6540
6541 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6542 if ( format.indexOf( "Z" ) > 0 )
6543 datetime = datetime.toUTC();
6544
6545 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6546 return locale.toString( datetime, format );
6547}
6548
6549static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6550{
6551 const QVariant variant = values.at( 0 );
6552 bool isQColor;
6553 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6554 if ( !color.isValid() )
6555 return QVariant();
6556
6557 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6558 if ( color.spec() == QColor::Spec::Cmyk )
6559 {
6560 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() )
6561 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6562 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6563 }
6564 else
6565 {
6566 const float avg = ( color.redF() + color.greenF() + color.blueF() )
6567 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6568 color.setRgbF( avg, avg, avg, alpha );
6569 }
6570
6571 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6572}
6573
6574static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6575{
6576 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6577 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6578 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6579 if ( ratio > 1 )
6580 {
6581 ratio = 1;
6582 }
6583 else if ( ratio < 0 )
6584 {
6585 ratio = 0;
6586 }
6587
6588 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6589 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6590 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6591 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6592
6593 QColor newColor( red, green, blue, alpha );
6594
6595 return QgsSymbolLayerUtils::encodeColor( newColor );
6596}
6597
6598static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6599{
6600 const QVariant variant1 = values.at( 0 );
6601 const QVariant variant2 = values.at( 1 );
6602
6603 if ( variant1.userType() != variant2.userType() )
6604 {
6605 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6606 return QVariant();
6607 }
6608
6609 bool isQColor;
6610 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6611 if ( !color1.isValid() )
6612 return QVariant();
6613
6614 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6615 if ( !color2.isValid() )
6616 return QVariant();
6617
6618 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6619 {
6620 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6621 return QVariant();
6622 }
6623
6624 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6625
6626 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6627 // NOLINTBEGIN(bugprone-narrowing-conversions)
6628
6629 QColor newColor;
6630 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6631 if ( color1.spec() == QColor::Spec::Cmyk )
6632 {
6633 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6634 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6635 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6636 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6637 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6638 }
6639 else
6640 {
6641 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6642 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6643 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6644 newColor = QColor::fromRgbF( red, green, blue, alpha );
6645 }
6646
6647 // NOLINTEND(bugprone-narrowing-conversions)
6648
6649 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6650}
6651
6652static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6653{
6654 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6655 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6656 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6657 QColor color = QColor( red, green, blue );
6658 if ( !color.isValid() )
6659 {
6660 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6661 color = QColor( 0, 0, 0 );
6662 }
6663
6664 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6665}
6666
6667static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6668{
6669 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6670 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6671 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6672 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6673 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6674 if ( !color.isValid() )
6675 {
6676 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6677 return QVariant();
6678 }
6679
6680 return color;
6681}
6682
6683static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6684{
6685 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6686 QVariant value = node->eval( parent, context );
6687 if ( parent->hasEvalError() )
6688 {
6689 parent->setEvalErrorString( QString() );
6690 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6692 value = node->eval( parent, context );
6694 }
6695 return value;
6696}
6697
6698static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6699{
6700 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6702 QVariant value = node->eval( parent, context );
6704 if ( value.toBool() )
6705 {
6706 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6708 value = node->eval( parent, context );
6710 }
6711 else
6712 {
6713 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6715 value = node->eval( parent, context );
6717 }
6718 return value;
6719}
6720
6721static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6722{
6723 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6724 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6725 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6726 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6727 QColor color = QColor( red, green, blue, alpha );
6728 if ( !color.isValid() )
6729 {
6730 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6731 color = QColor( 0, 0, 0 );
6732 }
6733 return QgsSymbolLayerUtils::encodeColor( color );
6734}
6735
6736QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6737{
6738 QgsGradientColorRamp expRamp;
6739 const QgsColorRamp *ramp = nullptr;
6740 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6741 {
6742 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6743 ramp = &expRamp;
6744 }
6745 else
6746 {
6747 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6748 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6749 if ( !ramp )
6751 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6752 return QVariant();
6753 }
6754 }
6755
6756 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6757 QColor color = ramp->color( value );
6758 return color;
6759}
6760
6761QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6762{
6763 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6764 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6765}
6766
6767static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6768{
6769 // Hue ranges from 0 - 360
6770 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6771 // Saturation ranges from 0 - 100
6772 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6773 // Lightness ranges from 0 - 100
6774 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6775
6776 QColor color = QColor::fromHslF( hue, saturation, lightness );
6777
6778 if ( !color.isValid() )
6779 {
6780 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6781 color = QColor( 0, 0, 0 );
6782 }
6783
6784 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6785}
6786
6787static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6788{
6789 // Hue ranges from 0 - 360
6790 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6791 // Saturation ranges from 0 - 100
6792 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6793 // Lightness ranges from 0 - 100
6794 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6795 // Alpha ranges from 0 - 255
6796 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6797
6798 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6799 if ( !color.isValid() )
6800 {
6801 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6802 color = QColor( 0, 0, 0 );
6803 }
6804 return QgsSymbolLayerUtils::encodeColor( color );
6805}
6806
6807static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6808{
6809 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6810 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6811 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6812 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6813
6814 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6815 if ( !color.isValid() )
6816 {
6817 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6818 return QVariant();
6819 }
6820
6821 return color;
6822}
6823
6824static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6825{
6826 // Hue ranges from 0 - 360
6827 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6828 // Saturation ranges from 0 - 100
6829 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6830 // Value ranges from 0 - 100
6831 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6832
6833 QColor color = QColor::fromHsvF( hue, saturation, value );
6834
6835 if ( !color.isValid() )
6836 {
6837 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6838 color = QColor( 0, 0, 0 );
6839 }
6840
6841 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6842}
6843
6844static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6845{
6846 // Hue ranges from 0 - 360
6847 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6848 // Saturation ranges from 0 - 100
6849 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6850 // Value ranges from 0 - 100
6851 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6852 // Alpha ranges from 0 - 255
6853 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6854
6855 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6856 if ( !color.isValid() )
6857 {
6858 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6859 color = QColor( 0, 0, 0 );
6860 }
6861 return QgsSymbolLayerUtils::encodeColor( color );
6862}
6863
6864static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6865{
6866 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6867 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6868 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6869 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6870 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6871
6872 if ( !color.isValid() )
6873 {
6874 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6875 return QVariant();
6876 }
6877
6878 return color;
6879}
6880
6881static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6882{
6883 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6884 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6885 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6886 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6887 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6888
6889 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6890 if ( !color.isValid() )
6891 {
6892 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6893 return QVariant();
6894 }
6895
6896 return color;
6897}
6898
6899static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6900{
6901 // Cyan ranges from 0 - 100
6902 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6903 // Magenta ranges from 0 - 100
6904 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6905 // Yellow ranges from 0 - 100
6906 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6907 // Black ranges from 0 - 100
6908 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6909
6910 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6911
6912 if ( !color.isValid() )
6913 {
6914 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6915 color = QColor( 0, 0, 0 );
6916 }
6917
6918 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6919}
6920
6921static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6922{
6923 // Cyan ranges from 0 - 100
6924 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6925 // Magenta ranges from 0 - 100
6926 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6927 // Yellow ranges from 0 - 100
6928 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6929 // Black ranges from 0 - 100
6930 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6931 // Alpha ranges from 0 - 255
6932 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6933
6934 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6935 if ( !color.isValid() )
6936 {
6937 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6938 color = QColor( 0, 0, 0 );
6939 }
6940 return QgsSymbolLayerUtils::encodeColor( color );
6941}
6942
6943static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6944{
6945 const QVariant variant = values.at( 0 );
6946 bool isQColor;
6947 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6948 if ( !color.isValid() )
6949 return QVariant();
6950
6951 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6952 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6953 return color.red();
6954 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6955 return color.green();
6956 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6957 return color.blue();
6958 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6959 return color.alpha();
6960 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6961 return static_cast< double >( color.hsvHueF() * 360 );
6962 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6963 return static_cast< double >( color.hsvSaturationF() * 100 );
6964 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6965 return static_cast< double >( color.valueF() * 100 );
6966 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6967 return static_cast< double >( color.hslHueF() * 360 );
6968 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6969 return static_cast< double >( color.hslSaturationF() * 100 );
6970 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6971 return static_cast< double >( color.lightnessF() * 100 );
6972 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6973 return static_cast< double >( color.cyanF() * 100 );
6974 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6975 return static_cast< double >( color.magentaF() * 100 );
6976 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6977 return static_cast< double >( color.yellowF() * 100 );
6978 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6979 return static_cast< double >( color.blackF() * 100 );
6980
6981 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6982 return QVariant();
6983}
6984
6985static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6986{
6987 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6988 if ( map.empty() )
6989 {
6990 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6991 return QVariant();
6992 }
6993
6994 QList< QColor > colors;
6996 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6997 {
6998 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6999 if ( !colors.last().isValid() )
7000 {
7001 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
7002 return QVariant();
7003 }
7004
7005 double step = it.key().toDouble();
7006 if ( it == map.constBegin() )
7007 {
7008 if ( step != 0.0 )
7009 stops << QgsGradientStop( step, colors.last() );
7010 }
7011 else if ( it == map.constEnd() )
7012 {
7013 if ( step != 1.0 )
7014 stops << QgsGradientStop( step, colors.last() );
7015 }
7016 else
7017 {
7018 stops << QgsGradientStop( step, colors.last() );
7019 }
7020 }
7021 bool discrete = values.at( 1 ).toBool();
7022
7023 if ( colors.empty() )
7024 return QVariant();
7025
7026 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
7027}
7028
7029static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7030{
7031 const QVariant variant = values.at( 0 );
7032 bool isQColor;
7033 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7034 if ( !color.isValid() )
7035 return QVariant();
7036
7037 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7038 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7039 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
7040 color.setRed( std::clamp( value, 0, 255 ) );
7041 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
7042 color.setGreen( std::clamp( value, 0, 255 ) );
7043 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
7044 color.setBlue( std::clamp( value, 0, 255 ) );
7045 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
7046 color.setAlpha( std::clamp( value, 0, 255 ) );
7047 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7048 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
7049 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7050 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
7051 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7052 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7053 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7054 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
7055 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7056 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
7057 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7058 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7059 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7060 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
7061 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7062 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
7063 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7064 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
7065 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7066 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7067 else
7068 {
7069 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7070 return QVariant();
7071 }
7072 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7073}
7074
7075static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7076{
7077 const QVariant variant = values.at( 0 );
7078 bool isQColor;
7079 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7080 if ( !color.isValid() )
7081 return QVariant();
7082
7083 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7084
7085 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7086}
7087
7088static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7089{
7090 const QVariant variant = values.at( 0 );
7091 bool isQColor;
7092 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7093 if ( !color.isValid() )
7094 return QVariant();
7095
7096 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7097
7098 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7099}
7100
7101static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7102{
7103 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7104 QgsGeometry geom = feat.geometry();
7105 if ( !geom.isNull() )
7106 return QVariant::fromValue( geom );
7107 return QVariant();
7108}
7109
7110static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7111{
7112 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7113 if ( !feat.isValid() )
7114 return QVariant();
7115 return feat.id();
7116}
7117
7118static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7119{
7120 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
7121 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
7122 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
7123
7124 if ( !sCrs.isValid() )
7125 return QVariant::fromValue( fGeom );
7126
7127 if ( !dCrs.isValid() )
7128 return QVariant::fromValue( fGeom );
7129
7131 if ( context )
7132 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
7133 QgsCoordinateTransform t( sCrs, dCrs, tContext );
7134 try
7135 {
7137 return QVariant::fromValue( fGeom );
7138 }
7139 catch ( QgsCsException &cse )
7140 {
7141 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
7142 return QVariant();
7143 }
7144 return QVariant();
7145}
7146
7147
7148static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7149{
7150 bool foundLayer = false;
7151 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7152
7153 //no layer found
7154 if ( !featureSource || !foundLayer )
7155 {
7156 return QVariant();
7157 }
7158
7159 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
7160
7162 req.setFilterFid( fid );
7163 req.setTimeout( 10000 );
7164 req.setRequestMayBeNested( true );
7165 if ( context )
7166 req.setFeedback( context->feedback() );
7167 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7168
7169 QgsFeature fet;
7170 QVariant result;
7171 if ( fIt.nextFeature( fet ) )
7172 result = QVariant::fromValue( fet );
7173
7174 return result;
7175}
7176
7177static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7178{
7179 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
7180 bool foundLayer = false;
7181 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7182
7183 //no layer found
7184 if ( !featureSource || !foundLayer )
7185 {
7186 return QVariant();
7187 }
7189 QString cacheValueKey;
7190 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7191 {
7192 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7193
7194 QMap<QString, QVariant>::const_iterator i = attributeMap.constBegin();
7195 QString filterString;
7196 for ( ; i != attributeMap.constEnd(); ++i )
7197 {
7198 if ( !filterString.isEmpty() )
7199 {
7200 filterString.append( " AND " );
7201 }
7202 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7203 }
7204 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7205 if ( context && context->hasCachedValue( cacheValueKey ) )
7206 {
7207 return context->cachedValue( cacheValueKey );
7208 }
7209 req.setFilterExpression( filterString );
7210 }
7211 else
7212 {
7213 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7214 int attributeId = featureSource->fields().lookupField( attribute );
7215 if ( attributeId == -1 )
7216 {
7217 return QVariant();
7218 }
7219
7220 const QVariant &attVal = values.at( 2 );
7221
7222 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7223 if ( context && context->hasCachedValue( cacheValueKey ) )
7224 {
7225 return context->cachedValue( cacheValueKey );
7226 }
7227
7229 }
7230 req.setLimit( 1 );
7231 req.setTimeout( 10000 );
7232 req.setRequestMayBeNested( true );
7233 if ( context )
7234 req.setFeedback( context->feedback() );
7235 if ( !parent->needsGeometry() )
7236 {
7238 }
7239 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7240
7241 QgsFeature fet;
7242 QVariant res;
7243 if ( fIt.nextFeature( fet ) )
7244 {
7245 res = QVariant::fromValue( fet );
7246 }
7247
7248 if ( context )
7249 context->setCachedValue( cacheValueKey, res );
7250 return res;
7251}
7252
7253static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7254{
7255 QVariant result;
7256 QString fieldName;
7257
7258 if ( context )
7259 {
7260 if ( !values.isEmpty() )
7261 {
7262 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7263 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7264 fieldName = col->name();
7265 else if ( values.size() == 2 )
7266 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7267 }
7268
7269 QVariant value = values.at( 0 );
7270
7271 const QgsFields fields = context->fields();
7272 int fieldIndex = fields.lookupField( fieldName );
7273
7274 if ( fieldIndex == -1 )
7275 {
7276 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7277 }
7278 else
7279 {
7280 // TODO this function is NOT thread safe
7282 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7284
7285 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7286 if ( context->hasCachedValue( cacheValueKey ) )
7287 {
7288 return context->cachedValue( cacheValueKey );
7289 }
7290
7291 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7293
7294 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7295
7296 QVariant cache;
7297 if ( !context->hasCachedValue( cacheKey ) )
7298 {
7299 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7300 context->setCachedValue( cacheKey, cache );
7301 }
7302 else
7303 cache = context->cachedValue( cacheKey );
7304
7305 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7306
7307 context->setCachedValue( cacheValueKey, result );
7308 }
7309 }
7310 else
7311 {
7312 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7313 }
7314
7315 return result;
7316}
7317
7318static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7319{
7320 const QVariant data = values.at( 0 );
7321 const QMimeDatabase db;
7322 return db.mimeTypeForData( data.toByteArray() ).name();
7323}
7324
7325static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7326{
7327 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7328
7329 bool foundLayer = false;
7330 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7331 values.at( 0 ),
7332 context,
7333 parent,
7334 [layerProperty]( QgsMapLayer *layer ) -> QVariant {
7335 if ( !layer )
7336 return QVariant();
7337
7338 // here, we always prefer the layer metadata values over the older server-specific published values
7339 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7340 return layer->name();
7341 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7342 return layer->id();
7343 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7344 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7345 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7346 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7347 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7348 {
7349 QStringList keywords;
7350 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7351 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7352 {
7353 keywords.append( it.value() );
7354 }
7355 if ( !keywords.isEmpty() )
7356 return keywords;
7357 return layer->serverProperties()->keywordList();
7358 }
7359 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7360 return layer->serverProperties()->dataUrl();
7361 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7362 {
7363 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7364 }
7365 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7366 return layer->serverProperties()->attributionUrl();
7367 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7368 return layer->publicSource();
7369 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7370 return layer->minimumScale();
7371 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7372 return layer->maximumScale();
7373 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7374 return layer->isEditable();
7375 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7376 return layer->crs().authid();
7377 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7378 return layer->crs().toProj();
7379 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7380 return layer->crs().description();
7381 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7382 return layer->crs().ellipsoidAcronym();
7383 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7384 {
7385 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7386 QVariant result = QVariant::fromValue( extentGeom );
7387 return result;
7388 }
7389 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7390 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7391 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7392 {
7393 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7394 return decodedUri.value( u"path"_s );
7395 }
7396 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7397 {
7398 switch ( layer->type() )
7399 {
7401 return QCoreApplication::translate( "expressions", "Vector" );
7403 return QCoreApplication::translate( "expressions", "Raster" );
7405 return QCoreApplication::translate( "expressions", "Mesh" );
7407 return QCoreApplication::translate( "expressions", "Vector Tile" );
7409 return QCoreApplication::translate( "expressions", "Plugin" );
7411 return QCoreApplication::translate( "expressions", "Annotation" );
7413 return QCoreApplication::translate( "expressions", "Point Cloud" );
7415 return QCoreApplication::translate( "expressions", "Group" );
7417 return QCoreApplication::translate( "expressions", "Tiled Scene" );
7418 }
7419 }
7420 else
7421 {
7422 //vector layer methods
7423 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7424 if ( vLayer )
7425 {
7426 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7427 return vLayer->storageType();
7428 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7430 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7431 return QVariant::fromValue( vLayer->featureCount() );
7432 }
7433 }
7434
7435 return QVariant();
7436 },
7437 foundLayer
7438 );
7439
7440 if ( !foundLayer )
7441 return QVariant();
7442 else
7443 return res;
7444}
7445
7446static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7447{
7448 const QString uriPart = values.at( 1 ).toString();
7449
7450 bool foundLayer = false;
7451
7452 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7453 values.at( 0 ),
7454 context,
7455 parent,
7456 [parent, uriPart]( QgsMapLayer *layer ) -> QVariant {
7457 if ( !layer->dataProvider() )
7458 {
7459 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7460 return QVariant();
7461 }
7462
7463 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7464
7465 if ( !uriPart.isNull() )
7466 {
7467 return decodedUri.value( uriPart );
7468 }
7469 else
7470 {
7471 return decodedUri;
7472 }
7473 },
7474 foundLayer
7475 );
7476
7477 if ( !foundLayer )
7478 {
7479 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7480 return QVariant();
7481 }
7482 else
7483 {
7484 return res;
7485 }
7486}
7487
7488static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7489{
7490 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7491 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7492
7493 bool foundLayer = false;
7494 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7495 values.at( 0 ),
7496 context,
7497 parent,
7498 [parent, band, layerProperty]( QgsMapLayer *layer ) -> QVariant {
7499 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7500 if ( !rl )
7501 return QVariant();
7502
7503 if ( band < 1 || band > rl->bandCount() )
7504 {
7505 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7506 return QVariant();
7507 }
7508
7510
7511 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7513 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7515 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7517 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7519 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7521 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7523 else
7524 {
7525 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7526 return QVariant();
7527 }
7528
7529 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7530 switch ( stat )
7531 {
7533 return stats.mean;
7535 return stats.stdDev;
7537 return stats.minimumValue;
7539 return stats.maximumValue;
7541 return stats.range;
7543 return stats.sum;
7544 default:
7545 break;
7546 }
7547 return QVariant();
7548 },
7549 foundLayer
7550 );
7551
7552 if ( !foundLayer )
7553 {
7554#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7555 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7556#endif
7557 return QVariant();
7558 }
7559 else
7560 {
7561 return res;
7562 }
7563}
7564
7565static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7566{
7567 return values;
7568}
7569
7570static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7571{
7572 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7573 bool ascending = values.value( 1 ).toBool();
7574 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7575 return list;
7576}
7577
7578static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7579{
7580 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7581}
7582
7583static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7584{
7585 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7586}
7587
7588static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7589{
7590 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7591}
7592
7593static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7594{
7595 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7596 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7597 int match = 0;
7598 for ( const auto &item : listB )
7599 {
7600 if ( listA.contains( item ) )
7601 match++;
7602 }
7603
7604 return QVariant( match == listB.count() );
7605}
7606
7607static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7608{
7609 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7610}
7611
7612static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7613{
7614 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7615 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7616 if ( pos < list.length() && pos >= 0 )
7617 return list.at( pos );
7618 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7619 return list.at( list.length() + pos );
7620 return QVariant();
7621}
7622
7623static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7624{
7625 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7626 return list.value( 0 );
7627}
7628
7629static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7630{
7631 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7632 return list.value( list.size() - 1 );
7633}
7634
7635static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7636{
7637 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7638 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7639}
7640
7641static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7642{
7643 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7644 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7645}
7646
7647static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7648{
7649 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7650 int i = 0;
7651 double total = 0.0;
7652 for ( const QVariant &item : list )
7653 {
7654 switch ( item.userType() )
7655 {
7656 case QMetaType::Int:
7657 case QMetaType::UInt:
7658 case QMetaType::LongLong:
7659 case QMetaType::ULongLong:
7660 case QMetaType::Float:
7661 case QMetaType::Double:
7662 total += item.toDouble();
7663 ++i;
7664 break;
7665 }
7666 }
7667 return i == 0 ? QVariant() : total / i;
7668}
7669
7670static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7671{
7672 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7673 QVariantList numbers;
7674 for ( const auto &item : list )
7675 {
7676 switch ( item.userType() )
7677 {
7678 case QMetaType::Int:
7679 case QMetaType::UInt:
7680 case QMetaType::LongLong:
7681 case QMetaType::ULongLong:
7682 case QMetaType::Float:
7683 case QMetaType::Double:
7684 numbers.append( item );
7685 break;
7686 }
7687 }
7688 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7689 const int count = numbers.count();
7690 if ( count == 0 )
7691 {
7692 return QVariant();
7693 }
7694 else if ( count % 2 )
7695 {
7696 return numbers.at( count / 2 );
7697 }
7698 else
7699 {
7700 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7701 }
7702}
7703
7704static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7705{
7706 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7707 int i = 0;
7708 double total = 0.0;
7709 for ( const QVariant &item : list )
7710 {
7711 switch ( item.userType() )
7712 {
7713 case QMetaType::Int:
7714 case QMetaType::UInt:
7715 case QMetaType::LongLong:
7716 case QMetaType::ULongLong:
7717 case QMetaType::Float:
7718 case QMetaType::Double:
7719 total += item.toDouble();
7720 ++i;
7721 break;
7722 }
7723 }
7724 return i == 0 ? QVariant() : total;
7725}
7726
7727static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7728{
7729 QVariant result = value;
7730 ( void ) result.convert( static_cast<int>( type ) );
7731 return result;
7732}
7733
7734static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7735{
7736 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7737 QHash< QVariant, int > hash;
7738 for ( const auto &item : list )
7739 {
7740 ++hash[item];
7741 }
7742 const QList< int > occurrences = hash.values();
7743 if ( occurrences.empty() )
7744 return QVariantList();
7745
7746 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7747
7748 const QString option = values.at( 1 ).toString();
7749 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7750 {
7751 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7752 }
7753 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7754 {
7755 if ( hash.isEmpty() )
7756 return QVariant();
7757
7758 return QVariant( hash.key( maxValue ) );
7759 }
7760 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7761 {
7762 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7763 }
7764 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7765 {
7766 if ( maxValue * 2 <= list.size() )
7767 return QVariant();
7768
7769 return QVariant( hash.key( maxValue ) );
7770 }
7771 else
7772 {
7773 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7774 return QVariant();
7775 }
7776}
7777
7778static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7779{
7780 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7781 QHash< QVariant, int > hash;
7782 for ( const auto &item : list )
7783 {
7784 ++hash[item];
7785 }
7786 const QList< int > occurrences = hash.values();
7787 if ( occurrences.empty() )
7788 return QVariantList();
7789
7790 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7791
7792 const QString option = values.at( 1 ).toString();
7793 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7794 {
7795 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7796 }
7797 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7798 {
7799 if ( hash.isEmpty() )
7800 return QVariant();
7801
7802 return QVariant( hash.key( minValue ) );
7803 }
7804 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7805 {
7806 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7807 }
7808 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7809 {
7810 if ( hash.isEmpty() )
7811 return QVariant();
7812
7813 // Remove the majority, all others are minority
7814 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7815 if ( maxValue * 2 > list.size() )
7816 hash.remove( hash.key( maxValue ) );
7817
7818 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7819 }
7820 else
7821 {
7822 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7823 return QVariant();
7824 }
7825}
7826
7827static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7828{
7829 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7830 list.append( values.at( 1 ) );
7831 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7832}
7833
7834static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7835{
7836 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7837 list.prepend( values.at( 1 ) );
7838 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7839}
7840
7841static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7842{
7843 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7844 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7845 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7846}
7847
7848static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7849{
7850 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7851 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7852 if ( position < 0 )
7853 position = position + list.length();
7854 if ( position >= 0 && position < list.length() )
7855 list.removeAt( position );
7856 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7857}
7858
7859static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7860{
7861 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7862 return QVariant();
7863
7864 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7865
7866 const QVariant toRemove = values.at( 1 );
7867 if ( QgsVariantUtils::isNull( toRemove ) )
7868 {
7869 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant &element ) { return QgsVariantUtils::isNull( element ); } ), list.end() );
7870 }
7871 else
7872 {
7873 list.removeAll( toRemove );
7874 }
7875 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7876}
7877
7878static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7879{
7880 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7881 {
7882 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7883
7884 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7885 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7886 {
7887 int index = list.indexOf( it.key() );
7888 while ( index >= 0 )
7889 {
7890 list.replace( index, it.value() );
7891 index = list.indexOf( it.key() );
7892 }
7893 }
7894
7895 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7896 }
7897 else if ( values.count() == 3 )
7898 {
7899 QVariantList before;
7900 QVariantList after;
7901 bool isSingleReplacement = false;
7902
7903 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7904 {
7905 before = QVariantList() << values.at( 1 );
7906 }
7907 else
7908 {
7909 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7910 }
7911
7912 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7913 {
7914 after = QVariantList() << values.at( 2 );
7915 isSingleReplacement = true;
7916 }
7917 else
7918 {
7919 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7920 }
7921
7922 if ( !isSingleReplacement && before.length() != after.length() )
7923 {
7924 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7925 return QVariant();
7926 }
7927
7928 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7929 for ( int i = 0; i < before.length(); i++ )
7930 {
7931 int index = list.indexOf( before.at( i ) );
7932 while ( index >= 0 )
7933 {
7934 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7935 index = list.indexOf( before.at( i ) );
7936 }
7937 }
7938
7939 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7940 }
7941 else
7942 {
7943 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7944 return QVariant();
7945 }
7946}
7947
7948static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7949{
7950 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7951 QVariantList list_new;
7952
7953 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7954 {
7955 while ( list.removeOne( cur ) )
7956 {
7957 list_new.append( cur );
7958 }
7959 }
7960
7961 list_new.append( list );
7962
7963 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7964}
7965
7966static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7967{
7968 QVariantList list;
7969 for ( const QVariant &cur : values )
7970 {
7971 list += QgsExpressionUtils::getListValue( cur, parent );
7972 }
7973 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7974}
7975
7976static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7977{
7978 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7979 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7980 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7981 int slice_length = 0;
7982 // negative positions means positions taken relative to the end of the array
7983 if ( start_pos < 0 )
7984 {
7985 start_pos = list.length() + start_pos;
7986 }
7987 if ( end_pos >= 0 )
7988 {
7989 slice_length = end_pos - start_pos + 1;
7990 }
7991 else
7992 {
7993 slice_length = list.length() + end_pos - start_pos + 1;
7994 }
7995 //avoid negative lengths in QList.mid function
7996 if ( slice_length < 0 )
7997 {
7998 slice_length = 0;
7999 }
8000 list = list.mid( start_pos, slice_length );
8001 return list;
8002}
8003
8004static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8005{
8006 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8007 std::reverse( list.begin(), list.end() );
8008 return list;
8009}
8010
8011static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8012{
8013 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8014 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
8015 for ( const QVariant &cur : array2 )
8016 {
8017 if ( array1.contains( cur ) )
8018 return QVariant( true );
8019 }
8020 return QVariant( false );
8021}
8022
8023static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8024{
8025 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8026
8027 QVariantList distinct;
8028
8029 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8030 {
8031 if ( !distinct.contains( *it ) )
8032 {
8033 distinct += ( *it );
8034 }
8035 }
8036
8037 return distinct;
8038}
8039
8040static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8041{
8042 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8043 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8044 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8045
8046 QString str;
8047
8048 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8049 {
8050 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
8051 if ( it != ( array.constEnd() - 1 ) )
8052 {
8053 str += delimiter;
8054 }
8055 }
8056
8057 return QVariant( str );
8058}
8059
8060static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8061{
8062 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8063 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8064 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8065
8066 QStringList list = str.split( delimiter );
8067 QVariantList array;
8068
8069 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
8070 {
8071 array += ( !( *it ).isEmpty() ) ? *it : empty;
8072 }
8073
8074 return array;
8075}
8076
8077static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8078{
8079 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8080 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
8081 if ( document.isNull() )
8082 return QVariant();
8083
8084 return document.toVariant();
8085}
8086
8087static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8088{
8089 Q_UNUSED( parent )
8090 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
8091 return QString( document.toJson( QJsonDocument::Compact ) );
8092}
8093
8094static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8095{
8096 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8097 if ( str.isEmpty() )
8098 return QVariantMap();
8099 str = str.trimmed();
8100
8101 return QgsHstoreUtils::parse( str );
8102}
8103
8104static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8105{
8106 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8107 return QgsHstoreUtils::build( map );
8108}
8109
8110static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8111{
8112 QVariantMap result;
8113 for ( int i = 0; i + 1 < values.length(); i += 2 )
8114 {
8115 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
8116 }
8117 return result;
8118}
8119
8120static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8121{
8122 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8123 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8124 QVariantMap resultMap;
8125
8126 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8127 {
8128 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
8129 }
8130
8131 return resultMap;
8132}
8133
8134static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8135{
8136 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
8137}
8138
8139static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8140{
8141 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
8142}
8143
8144static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8145{
8146 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8147 map.remove( values.at( 1 ).toString() );
8148 return map;
8149}
8150
8151static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8152{
8153 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8154 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
8155 return map;
8156}
8157
8158static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8159{
8160 QVariantMap result;
8161 for ( const QVariant &cur : values )
8162 {
8163 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
8164 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
8165 result.insert( it.key(), it.value() );
8166 }
8167 return result;
8168}
8169
8170static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8171{
8172 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
8173}
8174
8175static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8176{
8177 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
8178}
8179
8180static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8181{
8182 const QString envVarName = values.at( 0 ).toString();
8183 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
8184 return QVariant();
8185
8186 return QProcessEnvironment::systemEnvironment().value( envVarName );
8187}
8188
8189static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8190{
8191 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8192 if ( parent->hasEvalError() )
8193 {
8194 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "base_file_name"_L1 ) );
8195 return QVariant();
8196 }
8197 return QFileInfo( file ).completeBaseName();
8198}
8199
8200static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8201{
8202 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8203 if ( parent->hasEvalError() )
8204 {
8205 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_suffix"_L1 ) );
8206 return QVariant();
8207 }
8208 return QFileInfo( file ).completeSuffix();
8209}
8210
8211static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8212{
8213 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8214 if ( parent->hasEvalError() )
8215 {
8216 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8217 return QVariant();
8218 }
8219 return QFileInfo::exists( file );
8220}
8221
8222static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8223{
8224 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8225 if ( parent->hasEvalError() )
8226 {
8227 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_name"_L1 ) );
8228 return QVariant();
8229 }
8230 return QFileInfo( file ).fileName();
8231}
8232
8233static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8234{
8235 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8236 if ( parent->hasEvalError() )
8237 {
8238 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_file"_L1 ) );
8239 return QVariant();
8240 }
8241 return QFileInfo( file ).isFile();
8242}
8243
8244static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8245{
8246 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8247 if ( parent->hasEvalError() )
8248 {
8249 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8250 return QVariant();
8251 }
8252 return QFileInfo( file ).isDir();
8253}
8254
8255static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8256{
8257 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8258 if ( parent->hasEvalError() )
8259 {
8260 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8261 return QVariant();
8262 }
8263 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8264}
8265
8266static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8267{
8268 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8269 if ( parent->hasEvalError() )
8270 {
8271 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8272 return QVariant();
8273 }
8274 return QFileInfo( file ).size();
8275}
8276
8277static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8278{
8279 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8280}
8281
8282static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8283{
8284 QVariant hash;
8285 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8286 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8287
8288 if ( method == "md4"_L1 )
8289 {
8290 hash = fcnHash( str, QCryptographicHash::Md4 );
8291 }
8292 else if ( method == "md5"_L1 )
8293 {
8294 hash = fcnHash( str, QCryptographicHash::Md5 );
8295 }
8296 else if ( method == "sha1"_L1 )
8297 {
8298 hash = fcnHash( str, QCryptographicHash::Sha1 );
8299 }
8300 else if ( method == "sha224"_L1 )
8301 {
8302 hash = fcnHash( str, QCryptographicHash::Sha224 );
8303 }
8304 else if ( method == "sha256"_L1 )
8305 {
8306 hash = fcnHash( str, QCryptographicHash::Sha256 );
8307 }
8308 else if ( method == "sha384"_L1 )
8309 {
8310 hash = fcnHash( str, QCryptographicHash::Sha384 );
8311 }
8312 else if ( method == "sha512"_L1 )
8313 {
8314 hash = fcnHash( str, QCryptographicHash::Sha512 );
8315 }
8316 else if ( method == "sha3_224"_L1 )
8317 {
8318 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8319 }
8320 else if ( method == "sha3_256"_L1 )
8321 {
8322 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8323 }
8324 else if ( method == "sha3_384"_L1 )
8325 {
8326 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8327 }
8328 else if ( method == "sha3_512"_L1 )
8329 {
8330 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8331 }
8332 else if ( method == "keccak_224"_L1 )
8333 {
8334 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8335 }
8336 else if ( method == "keccak_256"_L1 )
8337 {
8338 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8339 }
8340 else if ( method == "keccak_384"_L1 )
8341 {
8342 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8343 }
8344 else if ( method == "keccak_512"_L1 )
8345 {
8346 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8347 }
8348 else
8349 {
8350 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8351 }
8352 return hash;
8353}
8354
8355static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8356{
8357 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8358}
8359
8360static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8361{
8362 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8363}
8364
8365static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8366{
8367 const QByteArray input = values.at( 0 ).toByteArray();
8368 return QVariant( QString( input.toBase64() ) );
8369}
8370
8371static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8372{
8373 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8374 QUrlQuery query;
8375 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8376 {
8377 query.addQueryItem( it.key(), it.value().toString() );
8378 }
8379 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8381
8382static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8383{
8384 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8385 const QByteArray base64 = value.toLocal8Bit();
8386 const QByteArray decoded = QByteArray::fromBase64( base64 );
8387 return QVariant( decoded );
8388}
8389
8391typedef std::function<bool( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend )> RelationFunction;
8392
8393static QVariant executeGeomOverlay(
8394 const QVariantList &values,
8395 const QgsExpressionContext *context,
8396 QgsExpression *parent,
8397 const RelationFunction &relationFunction,
8398 bool invert = false,
8399 double bboxGrow = 0,
8400 bool isNearestFunc = false,
8401 bool isIntersectsFunc = false
8402)
8403{
8404 if ( !context )
8405 {
8406 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8407 return QVariant();
8408 }
8409
8410 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8411 // TODO this function is NOT thread safe
8413 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8415
8416 QgsFeatureRequest request;
8417 request.setTimeout( 10000 );
8418 request.setRequestMayBeNested( true );
8419 request.setFeedback( context->feedback() );
8420
8421 // First parameter is the overlay layer
8422 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8424
8425 const bool layerCanBeCached = node->isStatic( parent, context );
8426 QVariant targetLayerValue = node->eval( parent, context );
8428
8429 // Second parameter is the expression to evaluate (or null for testonly)
8430 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8432 QString subExpString = node->dump();
8433
8434 bool testOnly = ( subExpString == "NULL" );
8435 // TODO this function is NOT thread safe
8437 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8439 if ( !targetLayer ) // No layer, no joy
8440 {
8441 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8442 return QVariant();
8443 }
8444
8445 // Third parameter is the filtering expression
8446 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8448 QString filterString = node->dump();
8449 if ( filterString != "NULL" )
8450 {
8451 request.setFilterExpression( filterString ); //filter cached features
8452 }
8453
8454 // Fourth parameter is the limit
8455 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8457 QVariant limitValue = node->eval( parent, context );
8459 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8460
8461 double max_distance = 0;
8462 bool cacheEnabled = false;
8463
8464 double minOverlap { -1 };
8465 double minInscribedCircleRadius { -1 };
8466 bool returnDetails = false; //#spellok
8467 bool sortByMeasure = false;
8468 bool sortAscending = false;
8469 bool requireMeasures = false;
8470 bool overlapOrRadiusFilter = false;
8471
8473
8474 if ( isNearestFunc ) //maxdistance param handling
8475 {
8476 // Fifth parameter (for nearest only) is the max distance
8477 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8479 QVariant distanceValue = node->eval( parent, context );
8481 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8482
8483 // Sixth (for nearest only) parameter is the cache toggle
8484 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
8486 QVariant cacheValue = node->eval( parent, context );
8488 cacheEnabled = cacheValue.toBool();
8489 }
8490 else
8491 {
8492 // Fifth parameter is the cache toggle
8493 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8495 QVariant cacheValue = node->eval( parent, context );
8497 cacheEnabled = cacheValue.toBool();
8498
8499 // Sixth parameter is the min overlap (area or length)
8500 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8502 const QVariant minOverlapValue = node->eval( parent, context );
8504 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8505
8506 // Seventh parameter is the min inscribed circle radius
8507 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8509 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8511 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8512
8513 // Eighth parameter is the return_details
8514 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8515 // Return measures is only effective when an expression is set
8516 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8517
8518 // Ninth parameter is the sort_by_intersection_size flag
8519 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8520 // Sort by measures is only effective when an expression is set
8521 const QString sorting { node->eval( parent, context ).toString().toLower() };
8522 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8523 sortAscending = sorting.startsWith( "asc" );
8524 requireMeasures = sortByMeasure || returnDetails; //#spellok
8525 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8526
8527 // Tenth parameter is the geometry backend
8528 node = QgsExpressionUtils::getNode( values.at( 9 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8530 const QString backendStr = node->eval( parent, context ).toString().toUpper();
8532
8533 bool ok;
8534 backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
8535 if ( !ok )
8536 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
8537 }
8538
8539 FEAT_FROM_CONTEXT( context, feat )
8540 const QgsGeometry geometry = feat.geometry();
8541
8542 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8543 {
8544 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8545 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8546 }
8547
8548 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8549
8550 QgsRectangle intDomain = geometry.boundingBox();
8551 if ( bboxGrow != 0 )
8552 {
8553 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8554 }
8555
8556 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8557
8558 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8559 // Otherwise, it can be toggled by the user
8560 QgsSpatialIndex spatialIndex;
8561 QgsVectorLayer *cachedTarget;
8562 QList<QgsFeature> features;
8563 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8564 {
8565 // If the cache (local spatial index) is enabled, we materialize the whole
8566 // layer, then do the request on that layer instead.
8567 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8568 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8569
8570 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8571 {
8572 cachedTarget = targetLayer->materialize( request );
8573 if ( layerCanBeCached )
8574 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8575 }
8576 else
8577 {
8578 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8579 }
8580
8581 if ( !context->hasCachedValue( cacheIndex ) )
8582 {
8583 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8584 if ( layerCanBeCached )
8585 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8586 }
8587 else
8588 {
8589 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8590 }
8591
8592 QList<QgsFeatureId> fidsList;
8593 if ( isNearestFunc )
8594 {
8595 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8596 }
8597 else
8598 {
8599 fidsList = spatialIndex.intersects( intDomain );
8600 }
8601
8602 QListIterator<QgsFeatureId> i( fidsList );
8603 while ( i.hasNext() )
8604 {
8605 QgsFeatureId fId2 = i.next();
8606 if ( sameLayers && feat.id() == fId2 )
8607 continue;
8608 features.append( cachedTarget->getFeature( fId2 ) );
8609 }
8610 }
8611 else
8612 {
8613 // If the cache (local spatial index) is not enabled, we directly
8614 // get the features from the target layer
8615 request.setFilterRect( intDomain );
8616 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8617 QgsFeature feat2;
8618 while ( fit.nextFeature( feat2 ) )
8619 {
8620 if ( sameLayers && feat.id() == feat2.id() )
8621 continue;
8622 features.append( feat2 );
8623 }
8624 }
8625
8626 QgsExpression subExpression;
8627 QgsExpressionContext subContext;
8628 if ( !testOnly )
8629 {
8630 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8631 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8632
8633 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8634 {
8635 subExpression = QgsExpression( subExpString );
8637 subExpression.prepare( &subContext );
8638 }
8639 else
8640 {
8641 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8642 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8643 }
8644 }
8645
8646 // //////////////////////////////////////////////////////////////////
8647 // Helper functions for geometry tests
8648
8649 // Test function for linestring geometries, returns TRUE if test passes
8650 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double &overlapValue ) -> bool {
8651 bool testResult { false };
8652 // For return measures:
8653 QVector<double> overlapValues;
8654 const QgsGeometry merged { intersection.mergeLines() };
8655 for ( auto it = merged.const_parts_begin(); !testResult && it != merged.const_parts_end(); ++it )
8656 {
8658 // Check min overlap for intersection (if set)
8659 if ( minOverlap != -1 || requireMeasures )
8660 {
8661 overlapValue = geom->length();
8662 overlapValues.append( overlapValue );
8663 if ( minOverlap != -1 )
8664 {
8665 if ( overlapValue >= minOverlap )
8666 {
8667 testResult = true;
8668 }
8669 else
8670 {
8671 continue;
8672 }
8673 }
8674 }
8675 }
8676
8677 if ( !overlapValues.isEmpty() )
8678 {
8679 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8680 }
8681
8682 return testResult;
8683 };
8684
8685 // Test function for polygon geometries, returns TRUE if test passes
8686 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double &radiusValue, double &overlapValue ) -> bool {
8687 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8688 bool testResult { false };
8689 // For return measures:
8690 QVector<double> overlapValues;
8691 QVector<double> radiusValues;
8692 for ( auto it = intersection.const_parts_begin(); ( !testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8693 {
8695 // Check min overlap for intersection (if set)
8696 if ( minOverlap != -1 || requireMeasures )
8697 {
8698 overlapValue = geom->area();
8699 overlapValues.append( geom->area() );
8700 if ( minOverlap != -1 )
8701 {
8702 if ( overlapValue >= minOverlap )
8703 {
8704 testResult = true;
8705 }
8706 else
8707 {
8708 continue;
8709 }
8710 }
8711 }
8712
8713 // Check min inscribed circle radius for intersection (if set)
8714 if ( minInscribedCircleRadius != -1 || requireMeasures )
8715 {
8716 const QgsRectangle bbox = geom->boundingBox();
8717 const double width = bbox.width();
8718 const double height = bbox.height();
8719 const double size = width > height ? width : height;
8720 const double tolerance = size / 100.0;
8721 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8722 testResult = radiusValue >= minInscribedCircleRadius;
8723 radiusValues.append( radiusValues );
8724 }
8725 } // end for parts
8726
8727 // Get the max values
8728 if ( !radiusValues.isEmpty() )
8729 {
8730 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8731 }
8732
8733 if ( !overlapValues.isEmpty() )
8734 {
8735 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8736 }
8737
8738 return testResult;
8739 };
8740
8741
8742 bool found = false;
8743 int foundCount = 0;
8744 QVariantList results;
8745
8746 QListIterator<QgsFeature> i( features );
8747 try
8748 {
8749 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8750 {
8751 QgsFeature feat2 = i.next();
8752
8753
8754 if ( relationFunction( geometry, feat2.geometry(), values, backend ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8755 {
8756 double overlapValue = -1;
8757 double radiusValue = -1;
8758
8759 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8760 {
8761 QgsGeometry intersection { geometry.intersection( feat2.geometry(), QgsGeometryParameters() ) };
8762
8763 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8764 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8765 {
8766 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8767 intersection = QgsGeometry();
8768 QgsMultiPolygonXY poly;
8769 QgsMultiPolylineXY line;
8770 QgsMultiPointXY point;
8771 for ( const auto &geom : std::as_const( geometries ) )
8772 {
8773 switch ( geom.type() )
8774 {
8776 {
8777 poly.append( geom.asPolygon() );
8778 break;
8779 }
8781 {
8782 line.append( geom.asPolyline() );
8783 break;
8784 }
8786 {
8787 point.append( geom.asPoint() );
8788 break;
8789 }
8792 {
8793 break;
8794 }
8795 }
8796 }
8797
8798 switch ( geometry.type() )
8799 {
8801 {
8802 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8803 break;
8804 }
8806 {
8807 intersection = QgsGeometry::fromMultiPolylineXY( line );
8808 break;
8809 }
8811 {
8812 intersection = QgsGeometry::fromMultiPointXY( point );
8813 break;
8814 }
8817 {
8818 break;
8819 }
8820 }
8821 }
8822
8823 // Depending on the intersection geometry type and on the geometry type of
8824 // the tested geometry we can run different tests and collect different measures
8825 // that can be used for sorting (if required).
8826 switch ( intersection.type() )
8827 {
8829 {
8830 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8831 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8832
8833 if ( !testResult && overlapOrRadiusFilter )
8834 {
8835 continue;
8836 }
8837
8838 break;
8839 }
8840
8842 {
8843 // If the intersection is a linestring and a minimum circle is required
8844 // we can discard this result immediately.
8845 if ( minInscribedCircleRadius != -1 )
8846 {
8847 continue;
8848 }
8849
8850 // Otherwise a test for the overlap value is performed.
8851 const bool testResult { testLinestring( intersection, overlapValue ) };
8852
8853 if ( !testResult && overlapOrRadiusFilter )
8854 {
8855 continue;
8856 }
8857
8858 break;
8859 }
8860
8862 {
8863 // If the intersection is a point and a minimum circle is required
8864 // we can discard this result immediately.
8865 if ( minInscribedCircleRadius != -1 )
8866 {
8867 continue;
8868 }
8869
8870 bool testResult { false };
8871 if ( minOverlap != -1 || requireMeasures )
8872 {
8873 // Initially set this to 0 because it's a point intersection...
8874 overlapValue = 0;
8875 // ... but if the target geometry is not a point and the source
8876 // geometry is a point, we must record the length or the area
8877 // of the intersected geometry and use that as a measure for
8878 // sorting or reporting.
8879 if ( geometry.type() == Qgis::GeometryType::Point )
8880 {
8881 switch ( feat2.geometry().type() )
8882 {
8886 {
8887 break;
8888 }
8890 {
8891 testResult = testLinestring( feat2.geometry(), overlapValue );
8892 break;
8893 }
8895 {
8896 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8897 break;
8898 }
8899 }
8900 }
8901
8902 if ( !testResult && overlapOrRadiusFilter )
8903 {
8904 continue;
8905 }
8906 }
8907 break;
8908 }
8909
8912 {
8913 continue;
8914 }
8915 }
8916 }
8917
8918 found = true;
8919 foundCount++;
8920
8921 // We just want a single boolean result if there is any intersect: finish and return true
8922 if ( testOnly )
8923 break;
8924
8925 if ( !invert )
8926 {
8927 // We want a list of attributes / geometries / other expression values, evaluate now
8928 subContext.setFeature( feat2 );
8929 const QVariant expResult = subExpression.evaluate( &subContext );
8930
8931 if ( requireMeasures )
8932 {
8933 QVariantMap resultRecord;
8934 resultRecord.insert( u"id"_s, feat2.id() );
8935 resultRecord.insert( u"result"_s, expResult );
8936 // Overlap is always added because return measures was set
8937 resultRecord.insert( u"overlap"_s, overlapValue );
8938 // Radius is only added when is different than -1 (because for linestrings is not set)
8939 if ( radiusValue != -1 )
8940 {
8941 resultRecord.insert( u"radius"_s, radiusValue );
8942 }
8943 results.append( resultRecord );
8944 }
8945 else
8946 {
8947 results.append( expResult );
8948 }
8949 }
8950 else
8951 {
8952 // If not, results is a list of found ids, which we'll inverse and evaluate below
8953 results.append( feat2.id() );
8954 }
8955 }
8956 }
8957 }
8958 catch ( QgsException &e )
8959 {
8960 parent->setEvalErrorString( e.what() );
8961 return false;
8962 }
8963
8964 if ( testOnly )
8965 {
8966 if ( invert )
8967 found = !found; //for disjoint condition
8968 return found;
8969 }
8970
8971 if ( !invert )
8972 {
8973 if ( requireMeasures )
8974 {
8975 if ( sortByMeasure )
8976 {
8977 std::sort( results.begin(), results.end(), [sortAscending]( const QVariant &recordA, const QVariant &recordB ) -> bool {
8978 return sortAscending ? recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
8979 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
8980 } );
8981 }
8982 // Resize
8983 if ( limit > 0 && results.size() > limit )
8984 {
8985 results.erase( results.begin() + limit );
8986 }
8987
8988 if ( !returnDetails ) //#spellok
8989 {
8990 QVariantList expResults;
8991 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8992 {
8993 expResults.append( it->toMap().value( u"result"_s ) );
8994 }
8995 return expResults;
8996 }
8997 }
8998
8999 return results;
9000 }
9001
9002 // for disjoint condition returns the results for cached layers not intersected feats
9003 QVariantList disjoint_results;
9004 QgsFeature feat2;
9005 QgsFeatureRequest request2;
9006 request2.setLimit( limit );
9007 if ( context )
9008 request2.setFeedback( context->feedback() );
9009 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
9010 while ( fi.nextFeature( feat2 ) )
9011 {
9012 if ( !results.contains( feat2.id() ) )
9013 {
9014 subContext.setFeature( feat2 );
9015 disjoint_results.append( subExpression.evaluate( &subContext ) );
9016 }
9017 }
9018 return disjoint_results;
9019}
9020
9021// Intersect functions:
9022
9023static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9024{
9025 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9026 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, false, true );
9027}
9028
9029static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9030{
9031 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.contains( other ); };
9032 return executeGeomOverlay( values, context, parent, geomFunction );
9033}
9034
9035static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9036{
9037 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.crosses( other ); };
9038 return executeGeomOverlay( values, context, parent, geomFunction );
9039}
9040
9041static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9042{
9043 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9044 return geometry.isExactlyEqual( other, Qgis::GeometryBackend::QGIS );
9045 };
9046 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9047}
9048
9049static QVariant fcnGeomOverlayEqualsExact( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9050{
9051 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9052 return geometry.isExactlyEqual( other, backend );
9053 };
9054 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9055}
9056
9057static QVariant fcnGeomOverlayEqualsTopological( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9058{
9059 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9060 return geometry.isTopologicallyEqual( other, backend );
9061 };
9062 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9063}
9064
9065static QVariant fcnGeomOverlayEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9066{
9067 // This parameter is the epsilon tolerance
9068 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 10 ), parent );
9070 QVariant epsilonValue = node->eval( parent, context );
9072 double epsilon = QgsExpressionUtils::getDoubleValue( epsilonValue, parent );
9073
9074 RelationFunction geomFunction = [epsilon]( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9075 return geometry.isFuzzyEqual( other, epsilon, backend );
9076 };
9077 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9078}
9079
9080static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9081{
9082 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.touches( other ); };
9083 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9084}
9085
9086static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9087{
9088 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.within( other ); };
9089 return executeGeomOverlay( values, context, parent, geomFunction );
9090}
9091
9092static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9093{
9094 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9095 return executeGeomOverlay( values, context, parent, geomFunction, true, 0, false, true );
9096}
9097
9098static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9099{
9100 RelationFunction geomFunction = []( const QgsGeometry &, const QgsGeometry &, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9101 return true; // does nothing on purpose
9102 };
9103 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, true );
9104}
9105
9106const QList<QgsExpressionFunction *> &QgsExpression::Functions()
9107{
9108 // The construction of the list isn't thread-safe, and without the mutex,
9109 // crashes in the WFS provider may occur, since it can parse expressions
9110 // in parallel.
9111 // The mutex needs to be recursive.
9112 QMutexLocker locker( &sFunctionsMutex );
9113
9114 QList<QgsExpressionFunction *> &functions = *sFunctions();
9115
9116 if ( functions.isEmpty() )
9117 {
9119 << QgsExpressionFunction::Parameter( u"expression"_s )
9120 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
9121 << QgsExpressionFunction::Parameter( u"filter"_s, true );
9122
9123 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
9124 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true ) << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9125
9126 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
9127 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9128
9129 functions
9130 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
9131 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
9132 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
9133 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnAzimuth, u"GeometryGroup"_s )
9134 << new QgsStaticExpressionFunction(
9135 u"bearing"_s,
9137 << QgsExpressionFunction::Parameter( u"point_a"_s )
9138 << QgsExpressionFunction::Parameter( u"point_b"_s )
9139 << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() )
9140 << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ),
9141 fcnBearing,
9142 u"GeometryGroup"_s
9143 )
9144 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnInclination, u"GeometryGroup"_s )
9145 << new QgsStaticExpressionFunction(
9146 u"project"_s,
9148 << QgsExpressionFunction::Parameter( u"point"_s )
9149 << QgsExpressionFunction::Parameter( u"distance"_s )
9150 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9151 << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ),
9152 fcnProject,
9153 u"GeometryGroup"_s
9154 )
9155 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
9156 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
9157 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
9158 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
9159 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
9160 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
9161 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
9162 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
9163 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
9164 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
9165 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
9166 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
9167 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
9168
9169 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction(
9170 u"rand"_s,
9171 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9172 fcnRnd,
9173 u"Math"_s
9174 );
9175 randFunc->setIsStatic( false );
9176 functions << randFunc;
9177
9178 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction(
9179 u"randf"_s,
9181 << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 )
9182 << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 )
9183 << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9184 fcnRndF,
9185 u"Math"_s
9186 );
9187 randfFunc->setIsStatic( false );
9188 functions << randfFunc;
9189
9190 functions
9191 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9192 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9193 << 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 )
9194 << new QgsStaticExpressionFunction(
9195 u"scale_linear"_s,
9197 << QgsExpressionFunction::Parameter( u"value"_s )
9198 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9199 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9200 << QgsExpressionFunction::Parameter( u"range_min"_s )
9201 << QgsExpressionFunction::Parameter( u"range_max"_s ),
9202 fcnLinearScale,
9203 u"Math"_s
9204 )
9205 << new QgsStaticExpressionFunction(
9206 u"scale_polynomial"_s,
9208 << QgsExpressionFunction::Parameter( u"value"_s )
9209 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9210 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9211 << QgsExpressionFunction::Parameter( u"range_min"_s )
9212 << QgsExpressionFunction::Parameter( u"range_max"_s )
9213 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9214 fcnPolynomialScale,
9215 u"Math"_s,
9216 QString(),
9217 false,
9218 QSet<QString>(),
9219 false,
9220 QStringList() << u"scale_exp"_s
9221 )
9222 << new QgsStaticExpressionFunction(
9223 u"scale_exponential"_s,
9225 << QgsExpressionFunction::Parameter( u"value"_s )
9226 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9227 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9228 << QgsExpressionFunction::Parameter( u"range_min"_s )
9229 << QgsExpressionFunction::Parameter( u"range_max"_s )
9230 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9231 fcnExponentialScale,
9232 u"Math"_s
9233 )
9234 << new QgsStaticExpressionFunction(
9235 u"scale_cubic_bezier"_s,
9237 << QgsExpressionFunction::Parameter( u"value"_s )
9238 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9239 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9240 << QgsExpressionFunction::Parameter( u"range_min"_s )
9241 << QgsExpressionFunction::Parameter( u"range_max"_s )
9242 << QgsExpressionFunction::Parameter( u"x1"_s )
9243 << QgsExpressionFunction::Parameter( u"y1"_s )
9244 << QgsExpressionFunction::Parameter( u"x2"_s )
9245 << QgsExpressionFunction::Parameter( u"y2"_s ),
9246 fcnCubicBezierScale,
9247 u"Math"_s
9248 )
9249 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
9250 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
9251 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
9252 << 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 )
9253 << 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 )
9254 << 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 )
9255 << new QgsStaticExpressionFunction(
9256 u"to_string"_s,
9257 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9258 fcnToString,
9259 QStringList() << u"Conversions"_s << u"String"_s,
9260 QString(),
9261 false,
9262 QSet<QString>(),
9263 false,
9264 QStringList() << u"tostring"_s
9265 )
9266 << new QgsStaticExpressionFunction(
9267 u"to_datetime"_s,
9269 << QgsExpressionFunction::Parameter( u"value"_s )
9270 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9271 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9272 fcnToDateTime,
9273 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9274 QString(),
9275 false,
9276 QSet<QString>(),
9277 false,
9278 QStringList() << u"todatetime"_s
9279 )
9280 << new QgsStaticExpressionFunction(
9281 u"to_date"_s,
9283 << QgsExpressionFunction::Parameter( u"value"_s )
9284 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9285 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9286 fcnToDate,
9287 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9288 QString(),
9289 false,
9290 QSet<QString>(),
9291 false,
9292 QStringList() << u"todate"_s
9293 )
9294 << new QgsStaticExpressionFunction(
9295 u"to_time"_s,
9297 << QgsExpressionFunction::Parameter( u"value"_s )
9298 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9299 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9300 fcnToTime,
9301 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9302 QString(),
9303 false,
9304 QSet<QString>(),
9305 false,
9306 QStringList() << u"totime"_s
9307 )
9308 << new QgsStaticExpressionFunction(
9309 u"to_interval"_s,
9310 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9311 fcnToInterval,
9312 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9313 QString(),
9314 false,
9315 QSet<QString>(),
9316 false,
9317 QStringList() << u"tointerval"_s
9318 )
9319 << new QgsStaticExpressionFunction(
9320 u"to_dm"_s,
9322 << QgsExpressionFunction::Parameter( u"value"_s )
9323 << QgsExpressionFunction::Parameter( u"axis"_s )
9324 << QgsExpressionFunction::Parameter( u"precision"_s )
9325 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9326 fcnToDegreeMinute,
9327 u"Conversions"_s,
9328 QString(),
9329 false,
9330 QSet<QString>(),
9331 false,
9332 QStringList() << u"todm"_s
9333 )
9334 << new QgsStaticExpressionFunction(
9335 u"to_dms"_s,
9337 << QgsExpressionFunction::Parameter( u"value"_s )
9338 << QgsExpressionFunction::Parameter( u"axis"_s )
9339 << QgsExpressionFunction::Parameter( u"precision"_s )
9340 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9341 fcnToDegreeMinuteSecond,
9342 u"Conversions"_s,
9343 QString(),
9344 false,
9345 QSet<QString>(),
9346 false,
9347 QStringList() << u"todms"_s
9348 )
9349 << 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 )
9350 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
9351 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
9352 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
9353 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9354 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
9355 << new QgsStaticExpressionFunction(
9356 u"if"_s,
9358 << QgsExpressionFunction::Parameter( u"condition"_s )
9359 << QgsExpressionFunction::Parameter( u"result_when_true"_s )
9360 << QgsExpressionFunction::Parameter( u"result_when_false"_s ),
9361 fcnIf,
9362 u"Conditionals"_s,
9363 QString(),
9364 false,
9365 QSet<QString>(),
9366 true
9367 )
9368 << new QgsStaticExpressionFunction(
9369 u"try"_s,
9370 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ),
9371 fcnTry,
9372 u"Conditionals"_s,
9373 QString(),
9374 false,
9375 QSet<QString>(),
9376 true
9377 )
9378
9379 << new QgsStaticExpressionFunction(
9380 u"aggregate"_s,
9382 << QgsExpressionFunction::Parameter( u"layer"_s )
9383 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9384 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9385 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9386 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9387 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9388 fcnAggregate,
9389 u"Aggregates"_s,
9390 QString(),
9391 []( const QgsExpressionNodeFunction *node ) {
9392 // usesGeometry callback: return true if @parent variable is referenced
9393
9394 if ( !node )
9395 return true;
9396
9397 if ( !node->args() )
9398 return false;
9399
9400 QSet<QString> referencedVars;
9401 if ( node->args()->count() > 2 )
9402 {
9403 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9404 referencedVars = subExpressionNode->referencedVariables();
9405 }
9406
9407 if ( node->args()->count() > 3 )
9408 {
9409 QgsExpressionNode *filterNode = node->args()->at( 3 );
9410 referencedVars.unite( filterNode->referencedVariables() );
9411 }
9412 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
9413 },
9414 []( const QgsExpressionNodeFunction *node ) {
9415 // referencedColumns callback: return AllAttributes if @parent variable is referenced
9416
9417 if ( !node )
9418 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9419
9420 if ( !node->args() )
9421 return QSet<QString>();
9422
9423 QSet<QString> referencedCols;
9424 QSet<QString> referencedVars;
9425
9426 if ( node->args()->count() > 2 )
9427 {
9428 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9429 referencedVars = subExpressionNode->referencedVariables();
9430 referencedCols = subExpressionNode->referencedColumns();
9431 }
9432 if ( node->args()->count() > 3 )
9433 {
9434 QgsExpressionNode *filterNode = node->args()->at( 3 );
9435 referencedVars = filterNode->referencedVariables();
9436 referencedCols.unite( filterNode->referencedColumns() );
9437 }
9438
9439 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
9440 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9441 else
9442 return referencedCols;
9443 },
9444 true
9445 )
9446
9447 << new QgsStaticExpressionFunction(
9448 u"relation_aggregate"_s,
9450 << QgsExpressionFunction::Parameter( u"relation"_s )
9451 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9452 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9453 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9454 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9455 fcnAggregateRelation,
9456 u"Aggregates"_s,
9457 QString(),
9458 false,
9459 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9460 true
9461 )
9462
9463 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9464 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9465 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9466 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9467 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9468 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9469 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9470 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9471 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9472 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9473 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9474 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9475 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9476 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9477 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9478 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9479 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9480 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9481 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9482 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9483 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9484
9485 << 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 )
9486 << new QgsStaticExpressionFunction(
9487 u"regexp_matches"_s,
9489 << QgsExpressionFunction::Parameter( u"string"_s )
9490 << QgsExpressionFunction::Parameter( u"regex"_s )
9491 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
9492 fcnRegexpMatches,
9493 u"Arrays"_s
9494 )
9495
9496 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9497 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s ) << QgsExpressionFunction::Parameter( u"datetime2"_s ), fcnAge, u"Date and Time"_s )
9498 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9499 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9500 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9501 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9502 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9503 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9504 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9505 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9506 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9507 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9508 << 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 )
9509 << new QgsStaticExpressionFunction(
9510 u"make_time"_s,
9511 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s ) << QgsExpressionFunction::Parameter( u"minute"_s ) << QgsExpressionFunction::Parameter( u"second"_s ),
9512 fcnMakeTime,
9513 u"Date and Time"_s
9514 )
9515 << new QgsStaticExpressionFunction(
9516 u"make_datetime"_s,
9518 << QgsExpressionFunction::Parameter( u"year"_s )
9519 << QgsExpressionFunction::Parameter( u"month"_s )
9520 << QgsExpressionFunction::Parameter( u"day"_s )
9521 << QgsExpressionFunction::Parameter( u"hour"_s )
9522 << QgsExpressionFunction::Parameter( u"minute"_s )
9523 << QgsExpressionFunction::Parameter( u"second"_s ),
9524 fcnMakeDateTime,
9525 u"Date and Time"_s
9526 )
9527 << new QgsStaticExpressionFunction(
9528 u"make_interval"_s,
9530 << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9531 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9532 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9533 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9534 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9535 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9536 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9537 fcnMakeInterval,
9538 u"Date and Time"_s
9539 )
9540 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9541 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9542 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9543 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9544 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9545 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9546 << new QgsStaticExpressionFunction(
9547 u"substr_count"_s,
9549 << QgsExpressionFunction::Parameter( u"string"_s )
9550 << QgsExpressionFunction::Parameter( u"substring"_s )
9551 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9552 fcnSubstrCount,
9553 u"String"_s
9554 )
9555 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9556 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9557 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9558 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9559 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9560 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9561 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9562 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9563 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9564 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9565 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9566 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9567 << new QgsStaticExpressionFunction(
9568 u"wordwrap"_s,
9570 << QgsExpressionFunction::Parameter( u"text"_s )
9571 << QgsExpressionFunction::Parameter( u"length"_s )
9572 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ),
9573 fcnWordwrap,
9574 u"String"_s
9575 )
9576 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9577 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9578 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s ) }, fcnRepeat, u"String"_s )
9579 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9580 << new QgsStaticExpressionFunction(
9581 u"regexp_replace"_s,
9582 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"replacement"_s ),
9583 fcnRegexpReplace,
9584 u"String"_s
9585 )
9586 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9587 << new QgsStaticExpressionFunction(
9588 u"substr"_s,
9589 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ),
9590 fcnSubstr,
9591 u"String"_s,
9592 QString(),
9593 false,
9594 QSet< QString >(),
9595 false,
9596 QStringList(),
9597 true
9598 )
9599 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9600 << new QgsStaticExpressionFunction( u"concat_ws"_s, -1, fcnConcatWs, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9601 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9602 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9603 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9604 << 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 )
9605 << 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 )
9606 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9607 << new QgsStaticExpressionFunction(
9608 u"format_number"_s,
9610 << QgsExpressionFunction::Parameter( u"number"_s )
9611 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9612 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9613 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9614 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ),
9615 fcnFormatNumber,
9616 u"String"_s
9617 )
9618 << new QgsStaticExpressionFunction(
9619 u"format_date"_s,
9621 << QgsExpressionFunction::Parameter( u"datetime"_s )
9622 << QgsExpressionFunction::Parameter( u"format"_s )
9623 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9624 fcnFormatDate,
9625 QStringList() << u"String"_s << u"Date and Time"_s
9626 )
9627 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9628 << new QgsStaticExpressionFunction(
9629 u"color_mix_rgb"_s,
9630 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9631 fcnColorMixRgb,
9632 u"Color"_s
9633 )
9634 << new QgsStaticExpressionFunction(
9635 u"color_mix"_s,
9636 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9637 fcnColorMix,
9638 u"Color"_s
9639 )
9640 << 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 )
9641 << new QgsStaticExpressionFunction(
9642 u"color_rgbf"_s,
9644 << QgsExpressionFunction::Parameter( u"red"_s )
9645 << QgsExpressionFunction::Parameter( u"green"_s )
9646 << QgsExpressionFunction::Parameter( u"blue"_s )
9647 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9648 fcnColorRgbF,
9649 u"Color"_s
9650 )
9651 << new QgsStaticExpressionFunction(
9652 u"color_rgba"_s,
9654 << QgsExpressionFunction::Parameter( u"red"_s )
9655 << QgsExpressionFunction::Parameter( u"green"_s )
9656 << QgsExpressionFunction::Parameter( u"blue"_s )
9657 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9658 fncColorRgba,
9659 u"Color"_s
9660 )
9661 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColor, u"Color"_s )
9662 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColorObject, u"Color"_s )
9663 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ), fcnCreateRamp, u"Color"_s )
9664 << new QgsStaticExpressionFunction(
9665 u"color_hsl"_s,
9666 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"lightness"_s ),
9667 fcnColorHsl,
9668 u"Color"_s
9669 )
9670 << new QgsStaticExpressionFunction(
9671 u"color_hsla"_s,
9673 << QgsExpressionFunction::Parameter( u"hue"_s )
9674 << QgsExpressionFunction::Parameter( u"saturation"_s )
9675 << QgsExpressionFunction::Parameter( u"lightness"_s )
9676 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9677 fncColorHsla,
9678 u"Color"_s
9679 )
9680 << new QgsStaticExpressionFunction(
9681 u"color_hslf"_s,
9683 << QgsExpressionFunction::Parameter( u"hue"_s )
9684 << QgsExpressionFunction::Parameter( u"saturation"_s )
9685 << QgsExpressionFunction::Parameter( u"lightness"_s )
9686 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9687 fcnColorHslF,
9688 u"Color"_s
9689 )
9690 << new QgsStaticExpressionFunction(
9691 u"color_hsv"_s,
9692 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9693 fcnColorHsv,
9694 u"Color"_s
9695 )
9696 << new QgsStaticExpressionFunction(
9697 u"color_hsva"_s,
9699 << QgsExpressionFunction::Parameter( u"hue"_s )
9700 << QgsExpressionFunction::Parameter( u"saturation"_s )
9701 << QgsExpressionFunction::Parameter( u"value"_s )
9702 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9703 fncColorHsva,
9704 u"Color"_s
9705 )
9706 << new QgsStaticExpressionFunction(
9707 u"color_hsvf"_s,
9709 << QgsExpressionFunction::Parameter( u"hue"_s )
9710 << QgsExpressionFunction::Parameter( u"saturation"_s )
9711 << QgsExpressionFunction::Parameter( u"value"_s )
9712 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9713 fcnColorHsvF,
9714 u"Color"_s
9715 )
9716 << new QgsStaticExpressionFunction(
9717 u"color_cmyk"_s,
9719 << QgsExpressionFunction::Parameter( u"cyan"_s )
9720 << QgsExpressionFunction::Parameter( u"magenta"_s )
9721 << QgsExpressionFunction::Parameter( u"yellow"_s )
9722 << QgsExpressionFunction::Parameter( u"black"_s ),
9723 fcnColorCmyk,
9724 u"Color"_s
9725 )
9726 << new QgsStaticExpressionFunction(
9727 u"color_cmyka"_s,
9729 << QgsExpressionFunction::Parameter( u"cyan"_s )
9730 << QgsExpressionFunction::Parameter( u"magenta"_s )
9731 << QgsExpressionFunction::Parameter( u"yellow"_s )
9732 << QgsExpressionFunction::Parameter( u"black"_s )
9733 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9734 fncColorCmyka,
9735 u"Color"_s
9736 )
9737 << new QgsStaticExpressionFunction(
9738 u"color_cmykf"_s,
9740 << QgsExpressionFunction::Parameter( u"cyan"_s )
9741 << QgsExpressionFunction::Parameter( u"magenta"_s )
9742 << QgsExpressionFunction::Parameter( u"yellow"_s )
9743 << QgsExpressionFunction::Parameter( u"black"_s )
9744 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9745 fcnColorCmykF,
9746 u"Color"_s
9747 )
9748 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ), fncColorPart, u"Color"_s )
9749 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncDarker, u"Color"_s )
9750 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncLighter, u"Color"_s )
9751 << new QgsStaticExpressionFunction(
9752 u"set_color_part"_s,
9753 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9754 fncSetColorPart,
9755 u"Color"_s
9756 )
9757
9758 // file info
9759 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnBaseFileName, u"Files and Paths"_s )
9760 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSuffix, u"Files and Paths"_s )
9761 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileExists, u"Files and Paths"_s )
9762 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileName, u"Files and Paths"_s )
9763 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsFile, u"Files and Paths"_s )
9764 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsDir, u"Files and Paths"_s )
9765 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFilePath, u"Files and Paths"_s )
9766 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSize, u"Files and Paths"_s )
9767
9768 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ), fcnExif, u"Files and Paths"_s )
9769 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnExifGeoTag, u"GeometryGroup"_s )
9770
9771 // hash
9772 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ), fcnGenericHash, u"Conversions"_s )
9773 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashMd5, u"Conversions"_s )
9774 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashSha256, u"Conversions"_s )
9775
9776 //base64
9777 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBase64, u"Conversions"_s )
9778 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnFromBase64, u"Conversions"_s )
9779
9780 // magnetic models
9781 << new QgsStaticExpressionFunction(
9782 u"magnetic_declination"_s,
9784 << QgsExpressionFunction::Parameter( u"model_name"_s )
9785 << QgsExpressionFunction::Parameter( u"date"_s )
9786 << QgsExpressionFunction::Parameter( u"latitude"_s )
9787 << QgsExpressionFunction::Parameter( u"longitude"_s )
9788 << QgsExpressionFunction::Parameter( u"height"_s )
9789 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9790 fcnMagneticDeclination,
9791 u"MagneticModels"_s
9792 )
9793 << new QgsStaticExpressionFunction(
9794 u"magnetic_inclination"_s,
9796 << QgsExpressionFunction::Parameter( u"model_name"_s )
9797 << QgsExpressionFunction::Parameter( u"date"_s )
9798 << QgsExpressionFunction::Parameter( u"latitude"_s )
9799 << QgsExpressionFunction::Parameter( u"longitude"_s )
9800 << QgsExpressionFunction::Parameter( u"height"_s )
9801 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9802 fcnMagneticInclination,
9803 u"MagneticModels"_s
9804 )
9805 << new QgsStaticExpressionFunction(
9806 u"magnetic_declination_rate_of_change"_s,
9808 << QgsExpressionFunction::Parameter( u"model_name"_s )
9809 << QgsExpressionFunction::Parameter( u"date"_s )
9810 << QgsExpressionFunction::Parameter( u"latitude"_s )
9811 << QgsExpressionFunction::Parameter( u"longitude"_s )
9812 << QgsExpressionFunction::Parameter( u"height"_s )
9813 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9814 fcnMagneticDeclinationRateOfChange,
9815 u"MagneticModels"_s
9816 )
9817 << new QgsStaticExpressionFunction(
9818 u"magnetic_inclination_rate_of_change"_s,
9820 << QgsExpressionFunction::Parameter( u"model_name"_s )
9821 << QgsExpressionFunction::Parameter( u"date"_s )
9822 << QgsExpressionFunction::Parameter( u"latitude"_s )
9823 << QgsExpressionFunction::Parameter( u"longitude"_s )
9824 << QgsExpressionFunction::Parameter( u"height"_s )
9825 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9826 fcnMagneticInclinationRateOfChange,
9827 u"MagneticModels"_s
9828 )
9829
9830 // deprecated stuff - hidden from users
9831 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9832
9833 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9834 geomFunc->setIsStatic( false );
9835 functions << geomFunc;
9836
9837 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9838 areaFunc->setIsStatic( false );
9839 functions << areaFunc;
9840
9841 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9842
9843 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9844 lengthFunc->setIsStatic( false );
9845 functions << lengthFunc;
9846
9847 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9848 perimeterFunc->setIsStatic( false );
9849 functions << perimeterFunc;
9850
9851 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9852
9853 functions << new QgsStaticExpressionFunction( u"roundness"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnRoundness, u"GeometryGroup"_s );
9854
9855 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9856 xFunc->setIsStatic( false );
9857 functions << xFunc;
9858
9859 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9860 yFunc->setIsStatic( false );
9861 functions << yFunc;
9862
9863 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9864 zFunc->setIsStatic( false );
9865 functions << zFunc;
9866
9867 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions {
9868 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9869 { u"overlay_contains"_s, fcnGeomOverlayContains },
9870 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9871 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9872 { u"overlay_equals_exact"_s, fcnGeomOverlayEqualsExact },
9873 { u"overlay_equals_topological"_s, fcnGeomOverlayEqualsTopological },
9874 { u"overlay_equals_fuzzy"_s, fcnGeomOverlayEqualsFuzzy },
9875 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9876 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9877 { u"overlay_within"_s, fcnGeomOverlayWithin },
9878 };
9879 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9880 while ( i.hasNext() )
9881 {
9882 i.next();
9883 QString defaultBackend = i.key() == "overlay_equals"_L1 ? QString( "QGIS" ) : QString( "GEOS" );
9884 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction(
9885 i.key(),
9887 << QgsExpressionFunction::Parameter( u"layer"_s )
9888 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9889 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9890 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9891 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9892 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9893 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9894 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9895 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false )
9896 << QgsExpressionFunction::Parameter( u"backend"_s, true, defaultBackend, false )
9897 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4, false ),
9898 i.value(),
9899 u"GeometryGroup"_s,
9900 QString(),
9901 true,
9902 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9903 true
9904 );
9905
9906 // The current feature is accessed for the geometry, so this should not be cached
9907 fcnGeomOverlayFunc->setIsStatic( false );
9908 functions << fcnGeomOverlayFunc;
9909 }
9910
9911 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction(
9912 u"overlay_nearest"_s,
9914 << QgsExpressionFunction::Parameter( u"layer"_s )
9915 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9916 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9917 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9918 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9919 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9920 fcnGeomOverlayNearest,
9921 u"GeometryGroup"_s,
9922 QString(),
9923 true,
9924 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9925 true
9926 );
9927 // The current feature is accessed for the geometry, so this should not be cached
9928 fcnGeomOverlayNearestFunc->setIsStatic( false );
9929 functions << fcnGeomOverlayNearestFunc;
9930
9931 functions
9932 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9933 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9934 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
9935 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
9936 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
9937 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
9938 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
9939 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
9940 << 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 )
9941 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
9942 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
9943 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
9944 << 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 )
9945 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
9946 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
9947 << new QgsStaticExpressionFunction(
9948 u"make_triangle"_s,
9949 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ) << QgsExpressionFunction::Parameter( u"point3"_s ),
9950 fcnMakeTriangle,
9951 u"GeometryGroup"_s
9952 )
9953 << new QgsStaticExpressionFunction(
9954 u"make_circle"_s,
9956 << QgsExpressionFunction::Parameter( u"center"_s )
9957 << QgsExpressionFunction::Parameter( u"radius"_s )
9958 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9959 fcnMakeCircle,
9960 u"GeometryGroup"_s
9961 )
9962 << new QgsStaticExpressionFunction(
9963 u"make_ellipse"_s,
9965 << QgsExpressionFunction::Parameter( u"center"_s )
9966 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
9967 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
9968 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9969 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9970 fcnMakeEllipse,
9971 u"GeometryGroup"_s
9972 )
9973 << new QgsStaticExpressionFunction(
9974 u"make_regular_polygon"_s,
9976 << QgsExpressionFunction::Parameter( u"center"_s )
9977 << QgsExpressionFunction::Parameter( u"radius"_s )
9978 << QgsExpressionFunction::Parameter( u"number_sides"_s )
9979 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
9980 fcnMakeRegularPolygon,
9981 u"GeometryGroup"_s
9982 )
9983 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnMakeSquare, u"GeometryGroup"_s )
9984 << new QgsStaticExpressionFunction(
9985 u"make_rectangle_3points"_s,
9987 << QgsExpressionFunction::Parameter( u"point1"_s )
9988 << QgsExpressionFunction::Parameter( u"point2"_s )
9989 << QgsExpressionFunction::Parameter( u"point3"_s )
9990 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
9991 fcnMakeRectangleFrom3Points,
9992 u"GeometryGroup"_s
9993 )
9994 << new QgsStaticExpressionFunction(
9995 u"make_valid"_s,
9997 QgsExpressionFunction::Parameter( u"geometry"_s ),
9998#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
9999 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
10000#else
10001 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
10002#endif
10003 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
10004 },
10005 fcnGeomMakeValid,
10006 u"GeometryGroup"_s
10007 );
10008
10009 functions
10010 << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
10011 functions
10012 << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
10013 functions
10014 << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
10015 functions
10016 << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
10017
10018 QgsStaticExpressionFunction *xAtFunc
10019 = 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 );
10020 xAtFunc->setIsStatic( false );
10021 functions << xAtFunc;
10022
10023
10024 QgsStaticExpressionFunction *yAtFunc
10025 = 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 );
10026 yAtFunc->setIsStatic( false );
10027 functions << yAtFunc;
10028
10029 functions
10030 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
10031 << 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 )
10032 << 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 )
10033 << 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 )
10034 << 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 )
10035 << 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 )
10036 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10037 << 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 )
10038 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
10039 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
10040 << new QgsStaticExpressionFunction(
10041 u"intersects_bbox"_s,
10042 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10043 fcnBbox,
10044 u"GeometryGroup"_s,
10045 QString(),
10046 false,
10047 QSet<QString>(),
10048 false,
10049 QStringList() << u"bbox"_s
10050 )
10051 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDisjoint, u"GeometryGroup"_s )
10052 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersects, u"GeometryGroup"_s )
10053 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnTouches, u"GeometryGroup"_s )
10054 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCrosses, u"GeometryGroup"_s )
10055 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnContains, u"GeometryGroup"_s )
10056 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnOverlaps, u"GeometryGroup"_s )
10057 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnWithin, u"GeometryGroup"_s )
10058 << new QgsStaticExpressionFunction( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnEquals, u"GeometryGroup"_s )
10059 << new QgsStaticExpressionFunction(
10060 u"equals_exact"_s,
10062 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10063 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10064 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s ),
10065 fcnIsEqualsExact,
10066 u"GeometryGroup"_s
10067 )
10068 << new QgsStaticExpressionFunction(
10069 u"equals_topological"_s,
10071 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10072 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10073 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"GEOS"_s ),
10074 fcnIsEqualsTopological,
10075 u"GeometryGroup"_s
10076 )
10077 << new QgsStaticExpressionFunction(
10078 u"equals_fuzzy"_s,
10080 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10081 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10082 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s )
10083 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4 ),
10084 fcnIsEqualsFuzzy,
10085 u"GeometryGroup"_s
10086 )
10087 << 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 )
10088 << new QgsStaticExpressionFunction(
10089 u"rotate"_s,
10091 << QgsExpressionFunction::Parameter( u"geometry"_s )
10092 << QgsExpressionFunction::Parameter( u"rotation"_s )
10093 << QgsExpressionFunction::Parameter( u"center"_s, true )
10094 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
10095 fcnRotate,
10096 u"GeometryGroup"_s
10097 )
10098 << new QgsStaticExpressionFunction(
10099 u"scale"_s,
10101 << QgsExpressionFunction::Parameter( u"geometry"_s )
10102 << QgsExpressionFunction::Parameter( u"x_scale"_s )
10103 << QgsExpressionFunction::Parameter( u"y_scale"_s )
10104 << QgsExpressionFunction::Parameter( u"center"_s, true ),
10105 fcnScale,
10106 u"GeometryGroup"_s
10107 )
10108 << new QgsStaticExpressionFunction(
10109 u"affine_transform"_s,
10111 << QgsExpressionFunction::Parameter( u"geometry"_s )
10112 << QgsExpressionFunction::Parameter( u"delta_x"_s )
10113 << QgsExpressionFunction::Parameter( u"delta_y"_s )
10114 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
10115 << QgsExpressionFunction::Parameter( u"scale_x"_s )
10116 << QgsExpressionFunction::Parameter( u"scale_y"_s )
10117 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
10118 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
10119 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
10120 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
10121 fcnAffineTransform,
10122 u"GeometryGroup"_s
10123 )
10124 << new QgsStaticExpressionFunction(
10125 u"buffer"_s,
10127 << QgsExpressionFunction::Parameter( u"geometry"_s )
10128 << QgsExpressionFunction::Parameter( u"distance"_s )
10129 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
10130 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
10131 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
10132 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
10133 fcnBuffer,
10134 u"GeometryGroup"_s
10135 )
10136 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForceRHR, u"GeometryGroup"_s )
10137 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCW, u"GeometryGroup"_s )
10138 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCCW, u"GeometryGroup"_s )
10139 << new QgsStaticExpressionFunction(
10140 u"wedge_buffer"_s,
10142 << QgsExpressionFunction::Parameter( u"center"_s )
10143 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10144 << QgsExpressionFunction::Parameter( u"width"_s )
10145 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
10146 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ),
10147 fcnWedgeBuffer,
10148 u"GeometryGroup"_s
10149 )
10150 << new QgsStaticExpressionFunction(
10151 u"tapered_buffer"_s,
10153 << QgsExpressionFunction::Parameter( u"geometry"_s )
10154 << QgsExpressionFunction::Parameter( u"start_width"_s )
10155 << QgsExpressionFunction::Parameter( u"end_width"_s )
10156 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ),
10157 fcnTaperedBuffer,
10158 u"GeometryGroup"_s
10159 )
10160 << 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 )
10161 << new QgsStaticExpressionFunction(
10162 u"offset_curve"_s,
10164 << QgsExpressionFunction::Parameter( u"geometry"_s )
10165 << QgsExpressionFunction::Parameter( u"distance"_s )
10166 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10167 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10168 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10169 fcnOffsetCurve,
10170 u"GeometryGroup"_s
10171 )
10172 << new QgsStaticExpressionFunction(
10173 u"single_sided_buffer"_s,
10175 << QgsExpressionFunction::Parameter( u"geometry"_s )
10176 << QgsExpressionFunction::Parameter( u"distance"_s )
10177 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10178 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10179 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10180 fcnSingleSidedBuffer,
10181 u"GeometryGroup"_s
10182 )
10183 << new QgsStaticExpressionFunction(
10184 u"extend"_s,
10185 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10186 fcnExtend,
10187 u"GeometryGroup"_s
10188 )
10189 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
10190 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
10191 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
10192 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
10193 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
10194 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnInteriorRingN, u"GeometryGroup"_s )
10195 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnGeometryN, u"GeometryGroup"_s )
10196 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
10197 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
10198 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry1"_s ), QgsExpressionFunction::Parameter( u"geometry2"_s ) }, fcnSharedPaths, u"GeometryGroup"_s )
10199 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
10200 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
10201 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
10202 << new QgsStaticExpressionFunction(
10203 u"smooth"_s,
10205 << QgsExpressionFunction::Parameter( u"geometry"_s )
10206 << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
10207 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
10208 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
10209 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ),
10210 fcnSmooth,
10211 u"GeometryGroup"_s
10212 )
10213 << new QgsStaticExpressionFunction(
10214 u"triangular_wave"_s,
10215 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10216 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10217 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10218 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10219 fcnTriangularWave,
10220 u"GeometryGroup"_s
10221 )
10222 << new QgsStaticExpressionFunction(
10223 u"triangular_wave_randomized"_s,
10224 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10225 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10226 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10227 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10228 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10229 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10230 fcnTriangularWaveRandomized,
10231 u"GeometryGroup"_s
10232 )
10233 << new QgsStaticExpressionFunction(
10234 u"square_wave"_s,
10235 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10236 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10237 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10238 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10239 fcnSquareWave,
10240 u"GeometryGroup"_s
10241 )
10242 << new QgsStaticExpressionFunction(
10243 u"square_wave_randomized"_s,
10244 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10245 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10246 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10247 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10248 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10249 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10250 fcnSquareWaveRandomized,
10251 u"GeometryGroup"_s
10252 )
10253 << new QgsStaticExpressionFunction(
10254 u"wave"_s,
10255 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10256 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10257 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10258 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10259 fcnRoundWave,
10260 u"GeometryGroup"_s
10261 )
10262 << new QgsStaticExpressionFunction(
10263 u"wave_randomized"_s,
10264 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10265 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10266 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10267 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10268 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10269 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10270 fcnRoundWaveRandomized,
10271 u"GeometryGroup"_s
10272 )
10273 << new QgsStaticExpressionFunction(
10274 u"apply_dash_pattern"_s,
10275 {
10276 QgsExpressionFunction::Parameter( u"geometry"_s ),
10277 QgsExpressionFunction::Parameter( u"pattern"_s ),
10278 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
10279 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
10280 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
10281 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
10282 },
10283 fcnApplyDashPattern,
10284 u"GeometryGroup"_s
10285 )
10286 << new QgsStaticExpressionFunction( u"densify_by_count"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"vertices"_s ) }, fcnDensifyByCount, u"GeometryGroup"_s )
10287 << new QgsStaticExpressionFunction( u"densify_by_distance"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"distance"_s ) }, fcnDensifyByDistance, u"GeometryGroup"_s )
10288 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
10289 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
10290 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
10291 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
10292 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
10293 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
10294 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
10295 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
10296 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
10297 << new QgsStaticExpressionFunction(
10298 u"is_empty_or_null"_s,
10299 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
10300 fcnIsEmptyOrNull,
10301 u"GeometryGroup"_s,
10302 QString(),
10303 false,
10304 QSet<QString>(),
10305 false,
10306 QStringList(),
10307 true
10308 )
10309 << 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 )
10310#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
10311 << new QgsStaticExpressionFunction(
10312 u"concave_hull"_s,
10314 << QgsExpressionFunction::Parameter( u"geometry"_s )
10315 << QgsExpressionFunction::Parameter( u"target_percent"_s )
10316 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ),
10317 fcnConcaveHull,
10318 u"GeometryGroup"_s
10319 )
10320#endif
10321 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnOrientedBBox, u"GeometryGroup"_s )
10322 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMainAngle, u"GeometryGroup"_s )
10323 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ), fcnMinimalCircle, u"GeometryGroup"_s )
10324 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDifference, u"GeometryGroup"_s )
10325 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDistance, u"GeometryGroup"_s )
10326 << new QgsStaticExpressionFunction(
10327 u"hausdorff_distance"_s,
10329 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10330 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10331 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
10332 fcnHausdorffDistance,
10333 u"GeometryGroup"_s
10334 )
10335 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersection, u"GeometryGroup"_s )
10336 << new QgsStaticExpressionFunction(
10337 u"sym_difference"_s,
10338 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10339 fcnSymDifference,
10340 u"GeometryGroup"_s,
10341 QString(),
10342 false,
10343 QSet<QString>(),
10344 false,
10345 QStringList() << u"symDifference"_s
10346 )
10347 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10348 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10349 << new QgsStaticExpressionFunction(
10350 u"geom_to_wkt"_s,
10351 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
10352 fcnGeomToWKT,
10353 u"GeometryGroup"_s,
10354 QString(),
10355 false,
10356 QSet<QString>(),
10357 false,
10358 QStringList() << u"geomToWKT"_s
10359 )
10360 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10361 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
10362 << new QgsStaticExpressionFunction(
10363 u"transform"_s,
10364 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"source_auth_id"_s ) << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
10365 fcnTransformGeometry,
10366 u"GeometryGroup"_s
10367 )
10368 << new QgsStaticExpressionFunction(
10369 u"extrude"_s,
10370 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ),
10371 fcnExtrude,
10372 u"GeometryGroup"_s,
10373 QString()
10374 )
10375 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsMultipart, u"GeometryGroup"_s )
10376 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMax, u"GeometryGroup"_s )
10377 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMin, u"GeometryGroup"_s )
10378 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMax, u"GeometryGroup"_s )
10379 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMin, u"GeometryGroup"_s )
10380 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSinuosity, u"GeometryGroup"_s )
10381 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStraightDistance2d, u"GeometryGroup"_s );
10382
10383
10384 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction(
10385 u"order_parts"_s,
10387 << QgsExpressionFunction::Parameter( u"geometry"_s )
10388 << QgsExpressionFunction::Parameter( u"orderby"_s )
10389 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
10390 fcnOrderParts,
10391 u"GeometryGroup"_s,
10392 QString()
10393 );
10394
10395 orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10396 const QList< QgsExpressionNode *> argList = node->args()->list();
10397 for ( QgsExpressionNode *argNode : argList )
10398 {
10399 if ( !argNode->isStatic( parent, context ) )
10400 return false;
10401 }
10402
10403 if ( node->args()->count() > 1 )
10404 {
10405 QgsExpressionNode *argNode = node->args()->at( 1 );
10406
10407 QString expString = argNode->eval( parent, context ).toString();
10408
10409 QgsExpression e( expString );
10410
10411 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10412 return true;
10413 }
10414
10415 return true;
10416 } );
10417
10418 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10419 if ( node->args()->count() > 1 )
10420 {
10421 QgsExpressionNode *argNode = node->args()->at( 1 );
10422 QString expression = argNode->eval( parent, context ).toString();
10424 e.prepare( context );
10425 context->setCachedValue( expression, QVariant::fromValue( e ) );
10426 }
10427 return true;
10428 } );
10429 functions << orderPartsFunc;
10430
10431 functions
10432 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnClosestPoint, u"GeometryGroup"_s )
10433 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnShortestLine, u"GeometryGroup"_s )
10434 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
10435 << new QgsStaticExpressionFunction(
10436 u"line_interpolate_point_by_m"_s,
10438 << QgsExpressionFunction::Parameter( u"geometry"_s )
10439 << QgsExpressionFunction::Parameter( u"m"_s )
10440 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10441 fcnLineInterpolatePointByM,
10442 u"GeometryGroup"_s
10443 )
10444 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
10445 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
10446 << new QgsStaticExpressionFunction(
10447 u"line_locate_m"_s,
10449 << QgsExpressionFunction::Parameter( u"geometry"_s )
10450 << QgsExpressionFunction::Parameter( u"m"_s )
10451 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10452 fcnLineLocateM,
10453 u"GeometryGroup"_s
10454 )
10455 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
10456 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
10457 << new QgsStaticExpressionFunction(
10458 u"line_substring"_s,
10459 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10460 fcnLineSubset,
10461 u"GeometryGroup"_s
10462 );
10463
10464
10465 // **Record** functions
10466
10467 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
10468 idFunc->setIsStatic( false );
10469 functions << idFunc;
10470
10471 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
10472 currentFeatureFunc->setIsStatic( false );
10473 functions << currentFeatureFunc;
10474
10475 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction(
10476 u"uuid"_s,
10477 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ),
10478 fcnUuid,
10479 u"Record and Attributes"_s,
10480 QString(),
10481 false,
10482 QSet<QString>(),
10483 false,
10484 QStringList() << u"$uuid"_s
10485 );
10486 uuidFunc->setIsStatic( false );
10487 functions << uuidFunc;
10488
10489 functions
10490 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
10491 << new QgsStaticExpressionFunction(
10492 u"get_feature"_s,
10493 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"value"_s, true ),
10494 fcnGetFeature,
10495 u"Record and Attributes"_s,
10496 QString(),
10497 false,
10498 QSet<QString>(),
10499 false,
10500 QStringList() << u"QgsExpressionUtils::getFeature"_s
10501 )
10502 << new QgsStaticExpressionFunction(
10503 u"get_feature_by_id"_s,
10504 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"feature_id"_s ),
10505 fcnGetFeatureById,
10506 u"Record and Attributes"_s,
10507 QString(),
10508 false,
10509 QSet<QString>(),
10510 false
10511 );
10512
10513 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction(
10514 u"attributes"_s,
10515 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
10516 fcnAttributes,
10517 u"Record and Attributes"_s,
10518 QString(),
10519 false,
10520 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10521 );
10522 attributesFunc->setIsStatic( false );
10523 functions << attributesFunc;
10524 QgsStaticExpressionFunction *representAttributesFunc
10525 = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1, fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10526 representAttributesFunc->setIsStatic( false );
10527 functions << representAttributesFunc;
10528
10529 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction(
10530 u"is_feature_valid"_s,
10532 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10533 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10534 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10535 fcnValidateFeature,
10536 u"Record and Attributes"_s,
10537 QString(),
10538 false,
10539 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10540 );
10541 validateFeature->setIsStatic( false );
10542 functions << validateFeature;
10543
10544 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction(
10545 u"is_attribute_valid"_s,
10547 << QgsExpressionFunction::Parameter( u"attribute"_s, false )
10549 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10550 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10551 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10552 fcnValidateAttribute,
10553 u"Record and Attributes"_s,
10554 QString(),
10555 false,
10556 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10557 );
10558 validateAttribute->setIsStatic( false );
10559 functions << validateAttribute;
10560
10561 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction( u"maptip"_s, -1, fcnFeatureMaptip, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10562 maptipFunc->setIsStatic( false );
10563 functions << maptipFunc;
10564
10565 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction( u"display_expression"_s, -1, fcnFeatureDisplayExpression, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10566 displayFunc->setIsStatic( false );
10567 functions << displayFunc;
10568
10569 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( u"is_selected"_s, -1, fcnIsSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10570 isSelectedFunc->setIsStatic( false );
10571 functions << isSelectedFunc;
10572
10573 functions << new QgsStaticExpressionFunction( u"num_selected"_s, -1, fcnNumSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10574
10575 functions << new QgsStaticExpressionFunction(
10576 u"sqlite_fetch_and_increment"_s,
10578 << QgsExpressionFunction::Parameter( u"database"_s )
10579 << QgsExpressionFunction::Parameter( u"table"_s )
10580 << QgsExpressionFunction::Parameter( u"id_field"_s )
10581 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
10582 << QgsExpressionFunction::Parameter( u"filter_value"_s )
10583 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
10584 fcnSqliteFetchAndIncrement,
10585 u"Record and Attributes"_s
10586 );
10587
10588 // **CRS** functions
10589 functions
10590 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
10591 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
10592
10593
10594 // **Fields and Values** functions
10595 QgsStaticExpressionFunction *representValueFunc
10596 = 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 );
10597
10598 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10599 Q_UNUSED( context )
10600 if ( node->args()->count() == 1 )
10601 {
10602 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
10603 if ( colRef )
10604 {
10605 return true;
10606 }
10607 else
10608 {
10609 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
10610 return false;
10611 }
10612 }
10613 else if ( node->args()->count() == 2 )
10614 {
10615 return true;
10616 }
10617 else
10618 {
10619 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
10620 return false;
10621 }
10622 } );
10623
10624 functions << representValueFunc;
10625
10626 // **General** functions
10627 functions
10628 << new QgsStaticExpressionFunction( u"layer_property"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"property"_s ), fcnGetLayerProperty, u"Map Layers"_s )
10629 << new QgsStaticExpressionFunction( u"decode_uri"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"part"_s, true ), fcnDecodeUri, u"Map Layers"_s )
10630 << new QgsStaticExpressionFunction( u"mime_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary_data"_s ), fcnMimeType, u"General"_s )
10631 << new QgsStaticExpressionFunction(
10632 u"raster_statistic"_s,
10633 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"statistic"_s ),
10634 fcnGetRasterBandStat,
10635 u"Rasters"_s
10636 );
10637
10638 // **var** function
10639 QgsStaticExpressionFunction *varFunction
10640 = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
10641 varFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10642 /* A variable node is static if it has a static name and the name can be found at prepare
10643 * time and is tagged with isStatic.
10644 * It is not static if a variable is set during iteration or not tagged isStatic.
10645 * (e.g. geom_part variable)
10646 */
10647 if ( node->args()->count() > 0 )
10648 {
10649 QgsExpressionNode *argNode = node->args()->at( 0 );
10650
10651 if ( !argNode->isStatic( parent, context ) )
10652 return false;
10653
10654 const QString varName = argNode->eval( parent, context ).toString();
10655 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
10656 return false;
10657
10658 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
10659 return scope ? scope->isStatic( varName ) : false;
10660 }
10661 return false;
10662 } );
10663 varFunction->setUsesGeometryFunction( []( const QgsExpressionNodeFunction *node ) -> bool {
10664 if ( node && node->args()->count() > 0 )
10665 {
10666 QgsExpressionNode *argNode = node->args()->at( 0 );
10667 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
10668 {
10669 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
10670 return true;
10671 }
10672 }
10673 return false;
10674 } );
10675
10676 functions << varFunction;
10677
10678 QgsStaticExpressionFunction *evalTemplateFunction
10679 = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10680 evalTemplateFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10681 if ( node->args()->count() > 0 )
10682 {
10683 QgsExpressionNode *argNode = node->args()->at( 0 );
10684
10685 if ( argNode->isStatic( parent, context ) )
10686 {
10687 QString expString = argNode->eval( parent, context ).toString();
10688
10689 QgsExpression e( expString );
10690
10691 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10692 return true;
10693 }
10694 }
10695
10696 return false;
10697 } );
10698 functions << evalTemplateFunction;
10699
10700 QgsStaticExpressionFunction *evalFunc
10701 = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10702 evalFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10703 if ( node->args()->count() > 0 )
10704 {
10705 QgsExpressionNode *argNode = node->args()->at( 0 );
10706
10707 if ( argNode->isStatic( parent, context ) )
10708 {
10709 QString expString = argNode->eval( parent, context ).toString();
10710
10711 QgsExpression e( expString );
10712
10713 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10714 return true;
10715 }
10716 }
10717
10718 return false;
10719 } );
10720
10721 functions << evalFunc;
10722
10723 QgsStaticExpressionFunction *attributeFunc
10724 = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10725 attributeFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10726 const QList< QgsExpressionNode *> argList = node->args()->list();
10727 for ( QgsExpressionNode *argNode : argList )
10728 {
10729 if ( !argNode->isStatic( parent, context ) )
10730 return false;
10731 }
10732
10733 if ( node->args()->count() == 1 )
10734 {
10735 // not static -- this is the variant which uses the current feature taken direct from the expression context
10736 return false;
10737 }
10738
10739 return true;
10740 } );
10741 functions << attributeFunc;
10742
10743 functions
10744 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10745 << new QgsWithVariableExpressionFunction()
10746 << new QgsStaticExpressionFunction(
10747 u"raster_value"_s,
10748 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10749 fcnRasterValue,
10750 u"Rasters"_s
10751 )
10752 << new QgsStaticExpressionFunction(
10753 u"raster_attributes"_s,
10754 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10755 fcnRasterAttributes,
10756 u"Rasters"_s
10757 )
10758
10759 // functions for arrays
10760 << new QgsArrayForeachExpressionFunction()
10761 << new QgsArrayFilterExpressionFunction()
10762 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10763 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10764 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10765 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10766 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10767 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array_a"_s ) << QgsExpressionFunction::Parameter( u"array_b"_s ), fcnArrayAll, u"Arrays"_s )
10768 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10769 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10770 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10771 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10772 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10773 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10774 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10775 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10776 << 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 )
10777 << 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 )
10778 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10779 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10780 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10781 << new QgsStaticExpressionFunction(
10782 u"array_insert"_s,
10783 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10784 fcnArrayInsert,
10785 u"Arrays"_s
10786 )
10787 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10788 << new QgsStaticExpressionFunction(
10789 u"array_remove_all"_s,
10790 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10791 fcnArrayRemoveAll,
10792 u"Arrays"_s,
10793 QString(),
10794 false,
10795 QSet<QString>(),
10796 false,
10797 QStringList(),
10798 true
10799 )
10800 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10801 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"array_prioritize"_s ), fcnArrayPrioritize, u"Arrays"_s )
10802 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10803 << new QgsStaticExpressionFunction(
10804 u"array_slice"_s,
10805 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ),
10806 fcnArraySlice,
10807 u"Arrays"_s
10808 )
10809 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10810 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10811 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10812 << new QgsStaticExpressionFunction(
10813 u"array_to_string"_s,
10815 << QgsExpressionFunction::Parameter( u"array"_s )
10816 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10817 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10818 fcnArrayToString,
10819 u"Arrays"_s
10820 )
10821 << new QgsStaticExpressionFunction(
10822 u"string_to_array"_s,
10824 << QgsExpressionFunction::Parameter( u"string"_s )
10825 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10826 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10827 fcnStringToArray,
10828 u"Arrays"_s
10829 )
10830 << new QgsStaticExpressionFunction(
10831 u"generate_series"_s,
10832 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ),
10833 fcnGenerateSeries,
10834 u"Arrays"_s
10835 )
10836 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10837
10838 //functions for maps
10839 << 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 )
10840 << 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 )
10841 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10842 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10843 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10844 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10845 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10846 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10847 << 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 )
10848 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10849 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10850 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10851 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"prefix"_s ), fcnMapPrefixKeys, u"Maps"_s )
10852 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlTable, u"Maps"_s )
10853 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlDefinitionList, u"Maps"_s )
10854 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnToFormUrlEncode, u"Maps"_s )
10855
10856 ;
10857
10859
10860 //QgsExpression has ownership of all built-in functions
10861 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10862 {
10863 *sOwnedFunctions() << func;
10864 *sBuiltinFunctions() << func->name();
10865 sBuiltinFunctions()->append( func->aliases() );
10866 }
10867 }
10868 return functions;
10869}
10870
10871bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10872{
10873 int fnIdx = functionIndex( function->name() );
10874 if ( fnIdx != -1 )
10875 {
10876 return false;
10877 }
10878
10879 QMutexLocker locker( &sFunctionsMutex );
10880 sFunctions()->append( function );
10881 if ( transferOwnership )
10882 sOwnedFunctions()->append( function );
10883
10884 return true;
10885}
10886
10887bool QgsExpression::unregisterFunction( const QString &name )
10888{
10889 // You can never override the built in functions.
10890 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10891 {
10892 return false;
10893 }
10894 int fnIdx = functionIndex( name );
10895 if ( fnIdx != -1 )
10896 {
10897 QMutexLocker locker( &sFunctionsMutex );
10898 sFunctions()->removeAt( fnIdx );
10899 sFunctionIndexMap.clear();
10900 return true;
10901 }
10902 return false;
10903}
10904
10906{
10907 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10908 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10909 {
10910 sBuiltinFunctions()->removeAll( func->name() );
10911 for ( const QString &alias : func->aliases() )
10913 sBuiltinFunctions()->removeAll( alias );
10914 }
10915
10916 sFunctions()->removeAll( func );
10917 }
10918
10919 qDeleteAll( *sOwnedFunctions() );
10920 sOwnedFunctions()->clear();
10922
10923const QStringList &QgsExpression::BuiltinFunctions()
10924{
10925 if ( sBuiltinFunctions()->isEmpty() )
10926 {
10927 Functions(); // this method builds the gmBuiltinFunctions as well
10928 }
10929 return *sBuiltinFunctions();
10930}
10931
10934 u"array_foreach"_s, // skip-keyword-check
10935 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ),
10936 u"Arrays"_s
10937 )
10938{}
10939
10941{
10942 bool isStatic = false;
10943
10944 QgsExpressionNode::NodeList *args = node->args();
10946 if ( args->count() < 2 )
10947 return false;
10948
10949 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10950 {
10951 isStatic = true;
10952 }
10953 return isStatic;
10954}
10955
10957{
10958 Q_UNUSED( node )
10959 QVariantList result;
10960
10961 if ( args->count() < 2 )
10962 // error
10963 return result;
10964
10965 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10966
10967 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10968 std::unique_ptr< QgsExpressionContext > tempContext;
10969 if ( !subContext )
10970 {
10971 tempContext = std::make_unique< QgsExpressionContext >();
10972 subContext = tempContext.get();
10973 }
10974
10975 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10976 subContext->appendScope( subScope );
10977
10978 int i = 0;
10979 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
10980 {
10981 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
10982 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
10983 result << args->at( 1 )->eval( parent, subContext );
10984 }
10985
10986 if ( context )
10987 delete subContext->popScope();
10988
10989 return result;
10990}
10991
10992QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10994 // This is a dummy function, all the real handling is in run
10995 Q_UNUSED( values )
10996 Q_UNUSED( context )
10997 Q_UNUSED( parent )
10998 Q_UNUSED( node )
10999
11000 Q_ASSERT( false );
11001 return QVariant();
11002}
11003
11005{
11006 QgsExpressionNode::NodeList *args = node->args();
11007
11008 if ( args->count() < 2 )
11009 // error
11010 return false;
11011
11012 args->at( 0 )->prepare( parent, context );
11013
11014 QgsExpressionContext subContext;
11015 if ( context )
11016 subContext = *context;
11019 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11020 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
11021 subContext.appendScope( subScope );
11022
11023 args->at( 1 )->prepare( parent, &subContext );
11024
11025 return true;
11026}
11027
11029 : 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 )
11030{}
11031
11033{
11034 bool isStatic = false;
11035
11036 QgsExpressionNode::NodeList *args = node->args();
11038 if ( args->count() < 2 )
11039 return false;
11040
11041 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11042 {
11043 isStatic = true;
11044 }
11045 return isStatic;
11046}
11047
11049{
11050 Q_UNUSED( node )
11051 QVariantList result;
11052
11053 if ( args->count() < 2 )
11054 // error
11055 return result;
11056
11057 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11058
11059 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11060 std::unique_ptr< QgsExpressionContext > tempContext;
11061 if ( !subContext )
11062 {
11063 tempContext = std::make_unique< QgsExpressionContext >();
11064 subContext = tempContext.get();
11065 }
11066
11067 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11068 subContext->appendScope( subScope );
11069
11070 int limit = 0;
11071 if ( args->count() >= 3 )
11072 {
11073 const QVariant limitVar = args->at( 2 )->eval( parent, context );
11074
11075 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
11076 {
11077 limit = limitVar.toInt();
11078 }
11079 else
11080 {
11081 return result;
11082 }
11083 }
11084
11085 for ( const QVariant &value : array )
11086 {
11087 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
11088 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
11089 {
11090 result << value;
11091
11092 if ( limit > 0 && limit == result.size() )
11093 break;
11094 }
11095 }
11096
11097 if ( context )
11098 delete subContext->popScope();
11099
11100 return result;
11101}
11102
11103QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11105 // This is a dummy function, all the real handling is in run
11106 Q_UNUSED( values )
11107 Q_UNUSED( context )
11108 Q_UNUSED( parent )
11109 Q_UNUSED( node )
11110
11111 Q_ASSERT( false );
11112 return QVariant();
11113}
11114
11116{
11117 QgsExpressionNode::NodeList *args = node->args();
11118
11119 if ( args->count() < 2 )
11120 // error
11121 return false;
11122
11123 args->at( 0 )->prepare( parent, context );
11124
11125 QgsExpressionContext subContext;
11126 if ( context )
11127 subContext = *context;
11128
11130 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11131 subContext.appendScope( subScope );
11132
11133 args->at( 1 )->prepare( parent, &subContext );
11134
11135 return true;
11136}
11138 : 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 )
11139{}
11140
11142{
11143 bool isStatic = false;
11144
11145 QgsExpressionNode::NodeList *args = node->args();
11146
11147 if ( args->count() < 3 )
11148 return false;
11149
11150 // We only need to check if the node evaluation is static, if both - name and value - are static.
11151 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11152 {
11153 QVariant name = args->at( 0 )->eval( parent, context );
11154 QVariant value = args->at( 1 )->eval( parent, context );
11156 // Temporarily append a new scope to provide the variable
11157 appendTemporaryVariable( context, name.toString(), value );
11158 if ( args->at( 2 )->isStatic( parent, context ) )
11159 isStatic = true;
11160 popTemporaryVariable( context );
11161 }
11162
11163 return isStatic;
11164}
11165
11167{
11168 Q_UNUSED( node )
11169 QVariant result;
11170
11171 if ( args->count() < 3 )
11172 // error
11173 return result;
11174
11175 QVariant name = args->at( 0 )->eval( parent, context );
11176 QVariant value = args->at( 1 )->eval( parent, context );
11177
11178 const QgsExpressionContext *updatedContext = context;
11179 std::unique_ptr< QgsExpressionContext > tempContext;
11180 if ( !updatedContext )
11181 {
11182 tempContext = std::make_unique< QgsExpressionContext >();
11183 updatedContext = tempContext.get();
11185
11186 appendTemporaryVariable( updatedContext, name.toString(), value );
11187 result = args->at( 2 )->eval( parent, updatedContext );
11188
11189 if ( context )
11190 popTemporaryVariable( updatedContext );
11191
11192 return result;
11193}
11194
11195QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11197 // This is a dummy function, all the real handling is in run
11198 Q_UNUSED( values )
11199 Q_UNUSED( context )
11200 Q_UNUSED( parent )
11201 Q_UNUSED( node )
11202
11203 Q_ASSERT( false );
11204 return QVariant();
11205}
11206
11208{
11209 QgsExpressionNode::NodeList *args = node->args();
11210
11211 if ( args->count() < 3 )
11212 // error
11213 return false;
11214
11215 QVariant name = args->at( 0 )->prepare( parent, context );
11216 QVariant value = args->at( 1 )->prepare( parent, context );
11217
11218 const QgsExpressionContext *updatedContext = context;
11219 std::unique_ptr< QgsExpressionContext > tempContext;
11220 if ( !updatedContext )
11221 {
11222 tempContext = std::make_unique< QgsExpressionContext >();
11223 updatedContext = tempContext.get();
11224 }
11225
11226 appendTemporaryVariable( updatedContext, name.toString(), value );
11227 args->at( 2 )->prepare( parent, updatedContext );
11228
11229 if ( context )
11230 popTemporaryVariable( updatedContext );
11231
11232 return true;
11233}
11234
11235void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
11236{
11237 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11238 delete updatedContext->popScope();
11239}
11240
11241void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
11242{
11243 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
11244 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
11245
11246 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11247 updatedContext->appendScope( scope );
11248}
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:6453
@ StdDev
Standard deviation.
Definition qgis.h:6460
@ NoStatistic
No statistic.
Definition qgis.h:6454
@ 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:6330
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6347
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6344
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6337
@ Median
Median of values (numeric fields only).
Definition qgis.h:6338
@ Max
Max of values.
Definition qgis.h:6335
@ Min
Min of values.
Definition qgis.h:6334
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6348
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6341
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6352
@ Sum
Sum of values.
Definition qgis.h:6336
@ Minority
Minority of values.
Definition qgis.h:6342
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6333
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6351
@ Majority
Majority of values.
Definition qgis.h:6343
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6340
@ Count
Count.
Definition qgis.h:6331
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6345
@ CountDistinct
Number of distinct values.
Definition qgis.h:6332
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6349
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6350
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6346
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:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:272
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h: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 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:70
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
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.
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.
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.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry 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...
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.
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.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Represents a model of the Earth's magnetic field.
static bool fieldComponentsWithTimeDerivatives(double Bx, double By, double Bz, double Bxt, double Byt, double Bzt, double &H, double &F, double &D, double &I, double &Ht, double &Ft, double &Dt, double &It)
Compute various quantities dependent on a magnetic field and their rates of change.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h: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:722
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:588
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:734
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:7335
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7678
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7677
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7174
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7134
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:34