QGIS API Documentation 4.3.0-Master (59e977eef4b)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
6 email : matthias@opengis.ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
45#include "qgsmagneticmodel.h"
47#include "qgsmessagelog.h"
48#include "qgsmultilinestring.h"
49#include "qgsmultipoint.h"
50#include "qgsogcutils.h"
51#include "qgspolygon.h"
52#include "qgsproviderregistry.h"
53#include "qgsquadrilateral.h"
54#include "qgsrasterbandstats.h"
55#include "qgsrasterlayer.h"
56#include "qgsregularpolygon.h"
57#include "qgsspatialindex.h"
58#include "qgsstringutils.h"
59#include "qgsstyle.h"
60#include "qgssymbollayerutils.h"
61#include "qgsthreadingutils.h"
62#include "qgstransaction.h"
63#include "qgstriangle.h"
64#include "qgsunittypes.h"
65#include "qgsvariantutils.h"
66#include "qgsvectorlayer.h"
68#include "qgsvectorlayerutils.h"
69
70#include <QCryptographicHash>
71#include <QMimeDatabase>
72#include <QProcessEnvironment>
73#include <QRegularExpression>
74#include <QString>
75#include <QUrlQuery>
76#include <QUuid>
77
78using namespace Qt::StringLiterals;
79
80typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
81
83Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
85
88Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
89
90const QString QgsExpressionFunction::helpText() const
91{
92 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
93}
94
96{
97 Q_UNUSED( node )
98 // evaluate arguments
99 QVariantList argValues;
100 if ( args )
101 {
102 int arg = 0;
103 const QList< QgsExpressionNode * > argList = args->list();
104 argValues.reserve( argList.size() );
105 for ( QgsExpressionNode *n : argList )
106 {
107 QVariant v;
108 if ( lazyEval() )
109 {
110 // Pass in the node for the function to eval as it needs.
111 v = QVariant::fromValue( n );
112 }
113 else
114 {
115 v = n->eval( parent, context );
117 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
118 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
119 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
120 }
121 argValues.append( v );
122 arg++;
123 }
124 }
125
126 return func( argValues, context, parent, node );
127}
128
130{
131 Q_UNUSED( node )
132 return true;
133}
134
136{
137 return QStringList();
138}
139
141{
142 Q_UNUSED( parent )
143 Q_UNUSED( context )
144 Q_UNUSED( node )
145 return false;
146}
147
149{
150 Q_UNUSED( parent )
151 Q_UNUSED( context )
152 Q_UNUSED( node )
153 return true;
154}
155
157{
158 Q_UNUSED( node )
159 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
160}
161
163{
164 return mGroups.isEmpty() ? false : mGroups.contains( u"deprecated"_s );
165}
166
168{
169 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
170}
171
173{
174 return mHandlesNull;
175}
176
177// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
180 const QString &fnname,
182 FcnEval fcn,
183 const QString &group,
184 const QString &helpText,
185 const std::function< bool( const QgsExpressionNodeFunction *node ) > &usesGeometry,
186 const std::function< QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
187 bool lazyEval,
188 const QStringList &aliases,
189 bool handlesNull
190)
191 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
192 , mFnc( fcn )
193 , mAliases( aliases )
194 , mUsesGeometry( false )
195 , mUsesGeometryFunc( usesGeometry )
196 , mReferencedColumnsFunc( referencedColumns )
197{}
199
201{
202 return mAliases;
203}
204
206{
207 if ( mUsesGeometryFunc )
208 return mUsesGeometryFunc( node );
209 else
210 return mUsesGeometry;
211}
212
214{
215 mUsesGeometryFunc = usesGeometry;
216}
217
219{
220 if ( mReferencedColumnsFunc )
221 return mReferencedColumnsFunc( node );
222 else
223 return mReferencedColumns;
224}
225
227{
228 if ( mIsStaticFunc )
229 return mIsStaticFunc( node, parent, context );
230 else
231 return mIsStatic;
232}
233
235{
236 if ( mPrepareFunc )
237 return mPrepareFunc( node, parent, context );
238
239 return true;
240}
241
243{
244 mIsStaticFunc = isStatic;
245}
246
248{
249 mIsStaticFunc = nullptr;
250 mIsStatic = isStatic;
251}
252
253void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
254{
255 mPrepareFunc = prepareFunc;
256}
257
259{
260 if ( node && node->args() )
261 {
262 const QList< QgsExpressionNode * > argList = node->args()->list();
263 for ( QgsExpressionNode *argNode : argList )
264 {
265 if ( !argNode->isStatic( parent, context ) )
266 return false;
267 }
268 }
269
270 return true;
271}
272
273static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
274{
275 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
276 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
277 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
278
279 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
280 return QVariant();
281
282 QVariantList array;
283 int length = 1;
284
285 array << start;
286 double current = start + step;
287 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
288 {
289 array << current;
290 current += step;
291 length++;
292 }
293
294 return array;
295}
296
297static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
298{
299 if ( !context )
300 return QVariant();
301
302 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
303 if ( context->hasGeometry() )
304 return context->geometry();
305 else
306 {
307 FEAT_FROM_CONTEXT( context, f )
308 QgsGeometry geom = f.geometry();
309 if ( !geom.isNull() )
310 return QVariant::fromValue( geom );
311 else
312 return QVariant();
313 }
314}
315
316static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
317{
318 if ( !context )
319 return QVariant();
320
321 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
322
323 if ( name == "feature"_L1 )
324 {
325 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
326 }
327 else if ( name == "id"_L1 )
328 {
329 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
330 }
331 else if ( name == "geometry"_L1 )
332 {
333 return fcnGeometry( values, context, parent, node );
334 }
335 else
336 {
337 return context->variable( name );
338 }
339}
340
341static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
342{
343 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
344 return QgsExpression::replaceExpressionText( templateString, context );
345}
346
347static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
348{
349 if ( !context )
350 return QVariant();
351
352 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
353 QgsExpression expression( expString );
354 return expression.evaluate( context );
355}
356
357static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
358{
359 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
360 return QVariant( std::sqrt( x ) );
361}
362
363static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
364{
365 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
366 return QVariant( std::fabs( val ) );
367}
368
369static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
370{
371 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
372 return ( deg * M_PI ) / 180;
373}
374static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
375{
376 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
377 return ( 180 * rad ) / M_PI;
378}
379static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
380{
381 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
382 return QVariant( std::sin( x ) );
383}
384static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
385{
386 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
387 return QVariant( std::cos( x ) );
388}
389static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
390{
391 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
392 return QVariant( std::tan( x ) );
393}
394static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
395{
396 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
397 return QVariant( std::asin( x ) );
398}
399static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
400{
401 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
402 return QVariant( std::acos( x ) );
403}
404static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 return QVariant( std::atan( x ) );
408}
409static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
410{
411 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
412 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
413 return QVariant( std::atan2( y, x ) );
414}
415static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
416{
417 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
418 return QVariant( std::exp( x ) );
419}
420static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
421{
422 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
423 if ( x <= 0 )
424 return QVariant();
425 return QVariant( std::log( x ) );
426}
427static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
428{
429 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
430 if ( x <= 0 )
431 return QVariant();
432 return QVariant( log10( x ) );
433}
434static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
435{
436 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
437 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
438 if ( x <= 0 || b <= 0 )
439 return QVariant();
440 return QVariant( std::log( x ) / std::log( b ) );
441}
442static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
443{
444 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
445 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
446 if ( max < min )
447 return QVariant();
448
449 std::random_device rd;
450 std::mt19937_64 generator( rd() );
451
452 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
453 {
454 quint32 seed;
455 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
456 {
457 // if seed can be converted to int, we use as is
458 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
459 }
460 else
461 {
462 // if not, we hash string representation to int
463 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
464 std::hash<std::string> hasher;
465 seed = hasher( seedStr.toStdString() );
466 }
467 generator.seed( seed );
468 }
469
470 // Return a random double in the range [min, max] (inclusive)
471 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
472 return QVariant( min + f * ( max - min ) );
473}
474static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
475{
476 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
477 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
478 if ( max < min )
479 return QVariant();
480
481 std::random_device rd;
482 std::mt19937_64 generator( rd() );
483
484 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
485 {
486 quint32 seed;
487 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
488 {
489 // if seed can be converted to int, we use as is
490 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
491 }
492 else
493 {
494 // if not, we hash string representation to int
495 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
496 std::hash<std::string> hasher;
497 seed = hasher( seedStr.toStdString() );
498 }
499 generator.seed( seed );
500 }
501
502 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
503 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
504 return QVariant( randomInteger );
505
506 // Prevent wrong conversion of QVariant. See #36412
507 return QVariant( int( randomInteger ) );
508}
509
510static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
511{
512 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
513 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
514 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
515 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
516 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
517
518 if ( domainMin >= domainMax )
519 {
520 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
521 return QVariant();
522 }
523
524 // outside of domain?
525 if ( val >= domainMax )
526 {
527 return rangeMax;
528 }
529 else if ( val <= domainMin )
530 {
531 return rangeMin;
532 }
533
534 // calculate linear scale
535 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
536 double c = rangeMin - ( domainMin * m );
537
538 // Return linearly scaled value
539 return QVariant( m * val + c );
540}
541
542static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
543{
544 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
545 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
546 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
547 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
548 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
549 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
550
551 if ( domainMin >= domainMax )
552 {
553 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
554 return QVariant();
555 }
556 if ( exponent <= 0 )
557 {
558 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
559 return QVariant();
560 }
561
562 // outside of domain?
563 if ( val >= domainMax )
564 {
565 return rangeMax;
566 }
567 else if ( val <= domainMin )
568 {
569 return rangeMin;
570 }
571
572 // Return polynomially scaled value
573 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
574}
575
576static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
577{
578 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
579 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
580 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
581 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
582 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
583 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
584
585 if ( domainMin >= domainMax )
586 {
587 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
588 return QVariant();
589 }
590 if ( exponent <= 0 )
591 {
592 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
593 return QVariant();
594 }
595
596 // outside of domain?
597 if ( val >= domainMax )
598 {
599 return rangeMax;
600 }
601 else if ( val <= domainMin )
602 {
603 return rangeMin;
604 }
605
606 // Return exponentially scaled value
607 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
608 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
609}
610
611static QVariant fcnCubicBezierScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
612{
613 const double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
614 const double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
615 const double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
616 const double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
617 const double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
618
619 const double x1 = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
620 const double y1 = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
621 const double x2 = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
622 const double y2 = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
623
624 if ( x1 < 0.0 || x1 > 1.0 || y1 < 0.0 || y1 > 1.0 || x2 < 0.0 || x2 > 1.0 || y2 < 0.0 || y2 > 1.0 )
625 {
626 parent->setEvalErrorString( QObject::tr( "Cubic bezier control points must be between 0 and 1" ) );
627 return QVariant();
628 }
629
630 if ( domainMin >= domainMax )
631 {
632 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
633 return QVariant();
634 }
635
636 // outside of domain?
637 if ( val >= domainMax )
638 {
639 return rangeMax;
640 }
641 else if ( val <= domainMin )
642 {
643 return rangeMin;
644 }
645
646 // normalize input to [0, 1] range
647 const double t = ( val - domainMin ) / ( domainMax - domainMin );
648
649 // solve using UnitBezier approach (based on MapLibre native's implementation)
650 const double cx = 3.0 * x1;
651 const double bx = 3.0 * ( x2 - x1 ) - cx;
652 const double ax = 1.0 - cx - bx;
653 const double cy = 3.0 * y1;
654 const double by = 3.0 * ( y2 - y1 ) - cy;
655 const double ay = 1.0 - cy - by;
656
657 constexpr double epsilon = 1e-6;
658
659 // solve for s using Newton's method (8 iterations)
660 double s = t;
661 bool solved = false;
662 for ( int i = 0; i < 8; ++i )
663 {
664 const double x2val = ( ( ax * s + bx ) * s + cx ) * s - t;
665 if ( std::fabs( x2val ) < epsilon )
666 {
667 solved = true;
668 break;
669 }
670 const double d2 = ( 3.0 * ax * s + 2.0 * bx ) * s + cx;
671 if ( std::fabs( d2 ) < 1e-6 )
672 break;
673 s = s - x2val / d2;
674 }
675
676 if ( !solved )
677 {
678 // fallback to bisection approach
679 double t0 = 0.0;
680 double t1 = 1.0;
681 s = t;
682
683 if ( s < t0 )
684 {
685 s = t0;
686 solved = true;
687 }
688 else if ( s > t1 )
689 {
690 s = t1;
691 solved = true;
692 }
693
694 while ( !solved && t0 < t1 )
695 {
696 const double x2val = ( ( ax * s + bx ) * s + cx ) * s;
697 if ( std::fabs( x2val - t ) < epsilon )
698 {
699 solved = true;
700 break;
701 }
702 if ( t > x2val )
703 t0 = s;
704 else
705 t1 = s;
706 s = ( t1 - t0 ) * 0.5 + t0;
707 }
708 }
709
710 const double easedT = ( ( ay * s + by ) * s + cy ) * s;
711 return QVariant( ( rangeMax - rangeMin ) * easedT + rangeMin );
712}
713
714static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
715{
716 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
717 double maxVal = std::numeric_limits<double>::quiet_NaN();
718 for ( const QVariant &val : values )
719 {
720 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
721 if ( std::isnan( maxVal ) )
722 {
723 maxVal = testVal;
724 }
725 else if ( !std::isnan( testVal ) )
726 {
727 maxVal = std::max( maxVal, testVal );
728 }
729 }
730
731 if ( !std::isnan( maxVal ) )
732 {
733 result = QVariant( maxVal );
734 }
735 return result;
736}
737
738static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
739{
740 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
741 double minVal = std::numeric_limits<double>::quiet_NaN();
742 for ( const QVariant &val : values )
743 {
744 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
745 if ( std::isnan( minVal ) )
746 {
747 minVal = testVal;
748 }
749 else if ( !std::isnan( testVal ) )
750 {
751 minVal = std::min( minVal, testVal );
752 }
753 }
754
755 if ( !std::isnan( minVal ) )
756 {
757 result = QVariant( minVal );
758 }
759 return result;
760}
761
762static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
763{
764 //lazy eval, so we need to evaluate nodes now
765
766 //first node is layer id or name
767 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
769 QVariant value = node->eval( parent, context );
771
772 // TODO this expression function is NOT thread safe
774 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
776 if ( !vl )
777 {
778 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
779 return QVariant();
780 }
781
782 // second node is aggregate type
783 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
785 value = node->eval( parent, context );
787 bool ok = false;
788 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
789 if ( !ok )
790 {
791 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
792 return QVariant();
793 }
794
795 // third node is subexpression (or field name)
796 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
798 QString subExpression = node->dump();
799
801 //optional forth node is filter
802 if ( values.count() > 3 )
803 {
804 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
806 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
807 if ( !nl || nl->value().isValid() )
808 parameters.filter = node->dump();
809 }
810
811 //optional fifth node is concatenator
812 if ( values.count() > 4 )
813 {
814 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
816 value = node->eval( parent, context );
818 parameters.delimiter = value.toString();
819 }
820
821 //optional sixth node is order by
822 QString orderBy;
823 if ( values.count() > 5 )
824 {
825 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
827 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
828 if ( !nl || nl->value().isValid() )
829 {
830 orderBy = node->dump();
831 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
832 }
833 }
834
835 QString aggregateError;
836 QVariant result;
837 if ( context )
838 {
839 QString cacheKey;
840 QgsExpression subExp( subExpression );
841 QgsExpression filterExp( parameters.filter );
842
843 const QSet< QString > filterVars = filterExp.referencedVariables();
844 const QSet< QString > subExpVars = subExp.referencedVariables();
845 QSet<QString> allVars = filterVars + subExpVars;
846
847 bool isStatic = true;
848 if ( filterVars.contains( u"parent"_s ) || filterVars.contains( QString() ) || subExpVars.contains( u"parent"_s ) || subExpVars.contains( QString() ) )
849 {
850 isStatic = false;
851 }
852 else
853 {
854 for ( const QString &varName : allVars )
855 {
856 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
857 if ( scope && !scope->isStatic( varName ) )
858 {
859 isStatic = false;
860 break;
861 }
862 }
863 }
864
865 if ( isStatic && !parameters.orderBy.isEmpty() )
866 {
867 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
868 {
869 const QgsExpression &orderByExpression { orderByClause.expression() };
870 if ( orderByExpression.referencedVariables().contains( u"parent"_s ) || orderByExpression.referencedVariables().contains( QString() ) )
871 {
872 isStatic = false;
873 break;
874 }
875 }
876 }
877
878 if ( !isStatic )
879 {
880 bool ok = false;
881 const QString contextHash = context->uniqueHash( ok, allVars );
882 if ( ok )
883 {
884 cacheKey = u"aggfcn:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
885 }
886 }
887 else
888 {
889 cacheKey = u"aggfcn:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
890 }
891
892 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
893 {
894 return context->cachedValue( cacheKey );
895 }
896
897 QgsExpressionContext subContext( *context );
899 subScope->setVariable( u"parent"_s, context->feature(), true );
900 subContext.appendScope( subScope );
901 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
902
903 if ( ok && !cacheKey.isEmpty() )
904 {
905 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
906 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
907 // associated with it's calculation!
908 context->setCachedValue( cacheKey, result );
909 }
910 }
911 else
912 {
913 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
914 }
915 if ( !ok )
916 {
917 if ( !aggregateError.isEmpty() )
918 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
919 else
920 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
921 return QVariant();
922 }
923
924 return result;
925}
926
927static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
928{
929 if ( !context )
930 {
931 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
932 return QVariant();
933 }
934
935 // first step - find current layer
936
937 // TODO this expression function is NOT thread safe
939 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
941 if ( !vl )
942 {
943 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
944 return QVariant();
945 }
946
947 //lazy eval, so we need to evaluate nodes now
948
949 //first node is relation name
950 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
952 QVariant value = node->eval( parent, context );
954 QString relationId = value.toString();
955 // check relation exists
956 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
957 if ( !relation.isValid() || relation.referencedLayer() != vl )
958 {
959 // check for relations by name
960 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
961 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
962 {
963 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
964 return QVariant();
965 }
966 else
967 {
968 relation = relations.at( 0 );
969 }
970 }
971
972 QgsVectorLayer *childLayer = relation.referencingLayer();
973
974 // second node is aggregate type
975 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
977 value = node->eval( parent, context );
979 bool ok = false;
980 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
981 if ( !ok )
982 {
983 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
984 return QVariant();
985 }
986
987 //third node is subexpression (or field name)
988 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
990 QString subExpression = node->dump();
991
992 //optional fourth node is concatenator
994 if ( values.count() > 3 )
995 {
996 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
998 value = node->eval( parent, context );
1000 parameters.delimiter = value.toString();
1001 }
1002
1003 //optional fifth node is order by
1004 QString orderBy;
1005 if ( values.count() > 4 )
1006 {
1007 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
1009 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1010 if ( !nl || nl->value().isValid() )
1011 {
1012 orderBy = node->dump();
1013 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1014 }
1015 }
1016
1017 if ( !context->hasFeature() )
1018 return QVariant();
1019 QgsFeature f = context->feature();
1020
1021 parameters.filter = relation.getRelatedFeaturesFilter( f );
1022
1023 const QString cacheKey = u"relagg:%1%:%2:%3:%4:%5:%6"_s.arg( relationId, vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1024 if ( context->hasCachedValue( cacheKey ) )
1025 return context->cachedValue( cacheKey );
1026
1027 QVariant result;
1028 ok = false;
1029
1030
1031 QgsExpressionContext subContext( *context );
1032 QString error;
1033 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1034
1035 if ( !ok )
1036 {
1037 if ( !error.isEmpty() )
1038 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1039 else
1040 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1041 return QVariant();
1042 }
1043
1044 // cache value
1045 context->setCachedValue( cacheKey, result );
1046 return result;
1047}
1048
1049
1050static QVariant fcnAggregateGeneric(
1051 Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1
1052)
1053{
1054 if ( !context )
1055 {
1056 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1057 return QVariant();
1058 }
1059
1060 // first step - find current layer
1061
1062 // TODO this expression function is NOT thread safe
1064 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
1066 if ( !vl )
1067 {
1068 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1069 return QVariant();
1070 }
1071
1072 //lazy eval, so we need to evaluate nodes now
1073
1074 //first node is subexpression (or field name)
1075 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1077 QString subExpression = node->dump();
1078
1079 //optional second node is group by
1080 QString groupBy;
1081 if ( values.count() > 1 )
1082 {
1083 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
1085 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1086 if ( !nl || nl->value().isValid() )
1087 groupBy = node->dump();
1088 }
1089
1090 //optional third node is filter
1091 if ( values.count() > 2 )
1092 {
1093 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
1095 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1096 if ( !nl || nl->value().isValid() )
1097 parameters.filter = node->dump();
1098 }
1099
1100 //optional order by node, if supported
1101 QString orderBy;
1102 if ( orderByPos >= 0 && values.count() > orderByPos )
1103 {
1104 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
1106 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1107 if ( !nl || nl->value().isValid() )
1108 {
1109 orderBy = node->dump();
1110 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1111 }
1112 }
1113
1114 // build up filter with group by
1115
1116 // find current group by value
1117 if ( !groupBy.isEmpty() )
1118 {
1119 QgsExpression groupByExp( groupBy );
1120 QVariant groupByValue = groupByExp.evaluate( context );
1121 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy, QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s, QgsExpression::quotedValue( groupByValue ) );
1122 if ( !parameters.filter.isEmpty() )
1123 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1124 else
1125 parameters.filter = groupByClause;
1126 }
1127
1128 QgsExpression subExp( subExpression );
1129 QgsExpression filterExp( parameters.filter );
1130
1131 bool isStatic = true;
1132 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1133 for ( const QString &varName : refVars )
1134 {
1135 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1136 if ( scope && !scope->isStatic( varName ) )
1137 {
1138 isStatic = false;
1139 break;
1140 }
1141 }
1142
1143 QString cacheKey;
1144 if ( !isStatic )
1145 {
1146 bool ok = false;
1147 const QString contextHash = context->uniqueHash( ok, refVars );
1148 if ( ok )
1149 {
1150 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
1151 }
1152 }
1153 else
1154 {
1155 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1156 }
1157
1158 if ( context->hasCachedValue( cacheKey ) )
1159 return context->cachedValue( cacheKey );
1160
1161 QVariant result;
1162 bool ok = false;
1163
1164 QgsExpressionContext subContext( *context );
1166 subScope->setVariable( u"parent"_s, context->feature(), true );
1167 subContext.appendScope( subScope );
1168 QString error;
1169 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1170
1171 if ( !ok )
1172 {
1173 if ( !error.isEmpty() )
1174 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1175 else
1176 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1177 return QVariant();
1178 }
1179
1180 // cache value
1181 context->setCachedValue( cacheKey, result );
1182 return result;
1183}
1184
1185
1186static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1187{
1188 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1189}
1190
1191static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1192{
1193 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1194}
1195
1196static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1197{
1198 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1199}
1200
1201static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1202{
1203 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1204}
1205
1206static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1207{
1208 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1209}
1210
1211static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1212{
1213 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1214}
1215
1216static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1217{
1218 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1219}
1220
1221static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1222{
1223 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1224}
1225
1226static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1227{
1228 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1229}
1230
1231static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1232{
1233 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1234}
1235
1236static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1237{
1238 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1239}
1240
1241static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1244}
1245
1246static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1247{
1248 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1249}
1250
1251static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1252{
1253 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1254}
1255
1256static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1257{
1258 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1259}
1260
1261static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1262{
1263 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1264}
1265
1266static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1267{
1268 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1269}
1270
1271static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1272{
1273 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1274}
1275
1276static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1277{
1279
1280 //fourth node is concatenator
1281 if ( values.count() > 3 )
1282 {
1283 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1285 QVariant value = node->eval( parent, context );
1287 parameters.delimiter = value.toString();
1288 }
1289
1290 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1291}
1292
1293static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1294{
1296
1297 //fourth node is concatenator
1298 if ( values.count() > 3 )
1299 {
1300 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1302 QVariant value = node->eval( parent, context );
1304 parameters.delimiter = value.toString();
1305 }
1306
1307 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1308}
1309
1310static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1311{
1312 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1313}
1314
1315static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1316{
1317 if ( !context )
1318 return QVariant();
1319
1320 QVariant scale = context->variable( u"map_scale"_s );
1321 bool ok = false;
1322 if ( QgsVariantUtils::isNull( scale ) )
1323 return QVariant();
1324
1325 const double v = scale.toDouble( &ok );
1326 if ( ok )
1327 return v;
1328 return QVariant();
1329}
1330
1331static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1332{
1333 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1334 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1335 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1336
1337 // force testValue to sit inside the range specified by the min and max value
1338 if ( testValue <= minValue )
1339 {
1340 return QVariant( minValue );
1341 }
1342 else if ( testValue >= maxValue )
1343 {
1344 return QVariant( maxValue );
1345 }
1346 else
1347 {
1348 return QVariant( testValue );
1349 }
1350}
1351
1352static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1353{
1354 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1355 return QVariant( std::floor( x ) );
1356}
1357
1358static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1359{
1360 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1361 return QVariant( std::ceil( x ) );
1362}
1363
1364static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1365{
1366 const QVariant value = values.at( 0 );
1367 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1368 {
1369 return QVariant( false );
1370 }
1371 else if ( value.userType() == QMetaType::QString )
1372 {
1373 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1374 return QVariant( !value.toString().isEmpty() );
1375 }
1376 else if ( QgsExpressionUtils::isList( value ) )
1377 {
1378 return !value.toList().isEmpty();
1379 }
1380 return QVariant( value.toBool() );
1381}
1382static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1383{
1384 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1385}
1386static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1387{
1388 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1389}
1390static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1391{
1392 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1393}
1394
1395static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1396{
1397 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1398 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1399 if ( format.isEmpty() && !language.isEmpty() )
1400 {
1401 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1402 return QVariant( QDateTime() );
1403 }
1404
1405 if ( format.isEmpty() && language.isEmpty() )
1406 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1407
1408 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409 QLocale locale = QLocale();
1410 if ( !language.isEmpty() )
1411 {
1412 locale = QLocale( language );
1413 }
1414
1415 QDateTime datetime = locale.toDateTime( datetimestring, format );
1416 if ( !datetime.isValid() )
1417 {
1418 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1419 datetime = QDateTime();
1420 }
1421 return QVariant( datetime );
1422}
1423
1424static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1425{
1426 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1427 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1428 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1429
1430 const QDate date( year, month, day );
1431 if ( !date.isValid() )
1432 {
1433 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1434 return QVariant();
1435 }
1436 return QVariant( date );
1437}
1438
1439static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1440{
1441 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1442 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1443 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1444
1445 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1446 if ( !time.isValid() )
1447 {
1448 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1449 return QVariant();
1450 }
1451 return QVariant( time );
1452}
1453
1454static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1455{
1456 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1457 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1458 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1459 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1460 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1461 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1462
1463 const QDate date( year, month, day );
1464 if ( !date.isValid() )
1465 {
1466 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1467 return QVariant();
1468 }
1469 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1470 if ( !time.isValid() )
1471 {
1472 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1473 return QVariant();
1474 }
1475 return QVariant( QDateTime( date, time ) );
1476}
1477
1478static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1479{
1480 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1481
1482 QTimeZone tz;
1483
1484#if QT_FEATURE_timezone > 0
1485 if ( !timeZoneId.isEmpty() )
1486 {
1487 tz = QTimeZone( timeZoneId.toUtf8() );
1488 }
1489
1490 if ( !tz.isValid() )
1491 {
1492 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1493 return QVariant();
1494 }
1495
1496#else
1497 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1498#endif
1499 return QVariant::fromValue( tz );
1500}
1501
1502static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1503{
1504#if QT_FEATURE_timezone > 0
1505 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1506 if ( datetime.isValid() )
1507 {
1508 return QVariant::fromValue( datetime.timeZone() );
1509 }
1510 return QVariant();
1511#else
1512 Q_UNUSED( values )
1513 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1514 return QVariant();
1515#endif
1516}
1517
1518static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1519{
1520#if QT_FEATURE_timezone > 0
1521 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1522 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1523 if ( datetime.isValid() && tz.isValid() )
1524 {
1525 datetime.setTimeZone( tz );
1526 return QVariant::fromValue( datetime );
1527 }
1528 return QVariant();
1529#else
1530 Q_UNUSED( values )
1531 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1532 return QVariant();
1533#endif
1534}
1535
1536static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1537{
1538#if QT_FEATURE_timezone > 0
1539 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1540 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1541 if ( datetime.isValid() && tz.isValid() )
1542 {
1543 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1544 }
1545 return QVariant();
1546#else
1547 Q_UNUSED( values )
1548 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1549 return QVariant();
1550#endif
1551}
1552
1553static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1554{
1555#if QT_FEATURE_timezone > 0
1556 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1557 if ( timeZone.isValid() )
1558 {
1559 return QString( timeZone.id() );
1560 }
1561 return QVariant();
1562#else
1563 Q_UNUSED( values )
1564 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1565 return QVariant();
1566#endif
1567}
1568
1569static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1570{
1571 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1572 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1573 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1574 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1575 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1576 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1577 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1578
1579 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1580}
1581
1582static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1583{
1584 for ( const QVariant &value : values )
1585 {
1586 if ( QgsVariantUtils::isNull( value ) )
1587 continue;
1588 return value;
1589 }
1590 return QVariant();
1591}
1592
1593static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1594{
1595 const QVariant val1 = values.at( 0 );
1596 const QVariant val2 = values.at( 1 );
1597
1598 if ( val1 == val2 )
1599 return QVariant();
1600 else
1601 return val1;
1602}
1603
1604static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1605{
1606 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1607 return QVariant( str.toLower() );
1608}
1609static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1610{
1611 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1612 return QVariant( str.toUpper() );
1613}
1614static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1615{
1616 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1617 QStringList elems = str.split( ' ' );
1618 for ( int i = 0; i < elems.size(); i++ )
1619 {
1620 if ( elems[i].size() > 1 )
1621 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1622 }
1623 return QVariant( elems.join( ' '_L1 ) );
1624}
1625
1626static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1627{
1628 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1629 return QVariant( str.trimmed() );
1630}
1631
1632static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1633{
1634 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1635
1636 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1637
1638 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1639 str.replace( re, QString() );
1640 return QVariant( str );
1641}
1642
1643static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1644{
1645 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1646
1647 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1648
1649 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1650 str.replace( re, QString() );
1651 return QVariant( str );
1652}
1653
1654static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1655{
1656 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1657 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1658 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1659}
1660
1661static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1662{
1663 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1664 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1665 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1666}
1667
1668static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1669{
1670 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1671 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1672 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1673 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1674}
1675
1676static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1677{
1678 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1679 return QVariant( QgsStringUtils::soundex( string ) );
1680}
1681
1682static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1683{
1684 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1685 return QVariant( QString( character ) );
1686}
1687
1688static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689{
1690 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1691
1692 if ( value.isEmpty() )
1693 {
1694 return QVariant();
1695 }
1696
1697 int res = value.at( 0 ).unicode();
1698 return QVariant( res );
1699}
1700
1701static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1702{
1703 if ( values.length() == 2 || values.length() == 3 )
1704 {
1705 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1706 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1707
1708 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1709
1710 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1711 }
1712
1713 return QVariant();
1714}
1715
1716static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1717{
1718 // two variants, one for geometry, one for string
1719
1720 //geometry variant
1721 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1722 if ( !geom.isNull() )
1723 {
1724 if ( geom.type() == Qgis::GeometryType::Line )
1725 return QVariant( geom.length() );
1726 else
1727 return QVariant();
1728 }
1729
1730 //otherwise fall back to string variant
1731 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1732 return QVariant( str.length() );
1733}
1734
1735static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1736{
1737 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1738
1739 if ( geom.type() != Qgis::GeometryType::Line )
1740 return QVariant();
1741
1742 double totalLength = 0;
1743 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1744 {
1746 {
1747 totalLength += line->length3D();
1748 }
1749 else
1750 {
1751 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1752 totalLength += segmentized->length3D();
1753 }
1754 }
1755
1756 return totalLength;
1757}
1758
1759
1760static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1761{
1762 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1763 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1764 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1765}
1766
1767static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1768{
1769 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1770 {
1771 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1772 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1773 QVector< QPair< QString, QString > > mapItems;
1774
1775 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1776 {
1777 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1778 }
1779
1780 // larger keys should be replaced first since they may contain whole smaller keys
1781 std::sort( mapItems.begin(), mapItems.end(), []( const QPair< QString, QString > &pair1, const QPair< QString, QString > &pair2 ) { return ( pair1.first.length() > pair2.first.length() ); } );
1782
1783 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1784 {
1785 str = str.replace( it->first, it->second );
1786 }
1787
1788 return QVariant( str );
1789 }
1790 else if ( values.count() == 3 )
1791 {
1792 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1793 QVariantList before;
1794 QVariantList after;
1795 bool isSingleReplacement = false;
1796
1797 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1798 {
1799 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1800 }
1801 else
1802 {
1803 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1804 }
1805
1806 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1807 {
1808 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1809 isSingleReplacement = true;
1810 }
1811 else
1812 {
1813 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1814 }
1815
1816 if ( !isSingleReplacement && before.length() != after.length() )
1817 {
1818 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1819 return QVariant();
1820 }
1821
1822 for ( int i = 0; i < before.length(); i++ )
1823 {
1824 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1825 }
1826
1827 return QVariant( str );
1828 }
1829 else
1830 {
1831 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1832 return QVariant();
1833 }
1834}
1835
1836static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1837{
1838 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1839 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1840 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1841
1842 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1843 if ( !re.isValid() )
1844 {
1845 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1846 return QVariant();
1847 }
1848 return QVariant( str.replace( re, after ) );
1849}
1850
1851static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1852{
1853 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1854 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1855
1856 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1857 if ( !re.isValid() )
1858 {
1859 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1860 return QVariant();
1861 }
1862 return QVariant( ( str.indexOf( re ) + 1 ) );
1863}
1864
1865static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1866{
1867 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1868 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1869 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1870
1871 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1872 if ( !re.isValid() )
1873 {
1874 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1875 return QVariant();
1876 }
1877
1878 QRegularExpressionMatch matches = re.match( str );
1879 if ( matches.hasMatch() )
1880 {
1881 QVariantList array;
1882 QStringList list = matches.capturedTexts();
1883
1884 // Skip the first string to only return captured groups
1885 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1886 {
1887 array += ( !( *it ).isEmpty() ) ? *it : empty;
1888 }
1889
1890 return QVariant( array );
1891 }
1892 else
1893 {
1894 return QVariant();
1895 }
1896}
1897
1898static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1899{
1900 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1901 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1902
1903 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1904 if ( !re.isValid() )
1905 {
1906 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1907 return QVariant();
1908 }
1909
1910 // extract substring
1911 QRegularExpressionMatch match = re.match( str );
1912 if ( match.hasMatch() )
1913 {
1914 // return first capture
1915 if ( match.lastCapturedIndex() > 0 )
1916 {
1917 // a capture group was present, so use that
1918 return QVariant( match.captured( 1 ) );
1919 }
1920 else
1921 {
1922 // no capture group, so using all match
1923 return QVariant( match.captured( 0 ) );
1924 }
1925 }
1926 else
1927 {
1928 return QVariant( "" );
1929 }
1930}
1931
1932static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1933{
1934 const int version = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1935 QUuid uuid;
1936 switch ( version )
1937 {
1938 case 4:
1939 uuid = QUuid::createUuid();
1940 break;
1941 case 7:
1942#if QT_VERSION >= QT_VERSION_CHECK( 6, 9, 0 )
1943 uuid = QUuid::createUuidV7();
1944#else
1945 parent->setEvalErrorString( QObject::tr( "UUid version 7 is not supported on this QGIS build" ) );
1946 return QVariant();
1947#endif
1948 break;
1949 default:
1950 parent->setEvalErrorString( QObject::tr( "UUid version %1 is not supported" ).arg( version ) );
1951 return QVariant();
1952 }
1953
1954 const QString format = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1955 if ( format.compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1956 return uuid.toString( QUuid::StringFormat::WithoutBraces );
1957 else if ( format.compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1958 return uuid.toString( QUuid::StringFormat::Id128 );
1959 return uuid.toString();
1960}
1961
1962static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1963{
1964 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1965 return QVariant();
1966
1967 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1968 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1969
1970 int len = 0;
1971 if ( values.at( 2 ).isValid() )
1972 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1973 else
1974 len = str.size();
1975
1976 if ( from < 0 )
1977 {
1978 from = str.size() + from;
1979 if ( from < 0 )
1980 {
1981 from = 0;
1982 }
1983 }
1984 else if ( from > 0 )
1985 {
1986 //account for the fact that substr() starts at 1
1987 from -= 1;
1988 }
1989
1990 if ( len < 0 )
1991 {
1992 len = str.size() + len - from;
1993 if ( len < 0 )
1994 {
1995 len = 0;
1996 }
1997 }
1998
1999 return QVariant( str.mid( from, len ) );
2000}
2001static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2002{
2003 FEAT_FROM_CONTEXT( context, f )
2004 return QVariant( f.id() );
2005}
2006
2007static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2008{
2009 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2010 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
2011 bool foundLayer = false;
2012 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2013 values.at( 0 ),
2014 context,
2015 parent,
2016 [parent, bandNb, geom]( QgsMapLayer *mapLayer ) {
2017 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
2018 if ( !layer || !layer->dataProvider() )
2019 {
2020 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2021 return QVariant();
2022 }
2023
2024 if ( bandNb < 1 || bandNb > layer->bandCount() )
2025 {
2026 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
2027 return QVariant();
2028 }
2029
2030 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
2031 {
2032 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
2033 return QVariant();
2034 }
2035
2036 QgsPointXY point = geom.asPoint();
2037 if ( geom.isMultipart() )
2038 {
2039 QgsMultiPointXY multiPoint = geom.asMultiPoint();
2040 if ( multiPoint.count() == 1 )
2041 {
2042 point = multiPoint[0];
2043 }
2044 else
2045 {
2046 // if the geometry contains more than one part, return an undefined value
2047 return QVariant();
2048 }
2049 }
2050
2051 double value = layer->dataProvider()->sample( point, bandNb );
2052 return std::isnan( value ) ? QVariant() : value;
2053 },
2054 foundLayer
2055 );
2056
2057 if ( !foundLayer )
2058 {
2059 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2060 return QVariant();
2061 }
2062 else
2063 {
2064 return res;
2065 }
2066}
2067
2068static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2069{
2070 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2071 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2072
2073 bool foundLayer = false;
2074 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2075 values.at( 0 ),
2076 context,
2077 parent,
2078 [parent, bandNb, value]( QgsMapLayer *mapLayer ) -> QVariant {
2079 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
2080 if ( !layer || !layer->dataProvider() )
2081 {
2082 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2083 return QVariant();
2084 }
2085
2086 if ( bandNb < 1 || bandNb > layer->bandCount() )
2087 {
2088 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
2089 return QVariant();
2090 }
2091
2092 if ( std::isnan( value ) )
2093 {
2094 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
2095 return QVariant();
2096 }
2097
2098 if ( !layer->dataProvider()->attributeTable( bandNb ) )
2099 {
2100 return QVariant();
2101 }
2102
2103 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
2104 if ( data.isEmpty() )
2105 {
2106 return QVariant();
2107 }
2108
2109 QVariantMap result;
2110 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
2111 for ( int idx = 0; idx < static_cast<int>( fields.count() ) && idx < static_cast<int>( data.count() ); ++idx )
2112 {
2113 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
2114 if ( field.isColor() || field.isRamp() )
2115 {
2116 continue;
2117 }
2118 result.insert( fields.at( idx ).name, data.at( idx ) );
2119 }
2120
2121 return result;
2122 },
2123 foundLayer
2124 );
2125
2126 if ( !foundLayer )
2127 {
2128 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2129 return QVariant();
2130 }
2131 else
2132 {
2133 return res;
2134 }
2135}
2136
2137static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2138{
2139 if ( !context )
2140 return QVariant();
2141
2142 return context->feature();
2143}
2144
2145static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2146{
2147 QgsFeature feature;
2148 QString attr;
2149 if ( values.size() == 1 )
2150 {
2151 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2152 feature = context->feature();
2153 }
2154 else if ( values.size() == 2 )
2155 {
2156 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2157 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2158 }
2159 else
2160 {
2161 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2162 return QVariant();
2163 }
2164
2165 return feature.attribute( attr );
2166}
2167
2168static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2169{
2170 QString table { R"html(
2171 <table>
2172 <thead>
2173 <tr><th>%1</th></tr>
2174 </thead>
2175 <tbody>
2176 <tr><td>%2</td></tr>
2177 </tbody>
2178 </table>)html" };
2179 QVariantMap dict;
2180 if ( values.size() == 1 )
2181 {
2182 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2183 }
2184 else
2185 {
2186 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2187 return QVariant();
2188 }
2189
2190 if ( dict.isEmpty() )
2191 {
2192 return QVariant();
2193 }
2194
2195 QStringList headers;
2196 QStringList cells;
2197
2198 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2199 {
2200 headers.push_back( it.key().toHtmlEscaped() );
2201 cells.push_back( it.value().toString().toHtmlEscaped() );
2202 }
2203
2204 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2205}
2206
2207static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2208{
2209 QString table { R"html(
2210 <dl>
2211 %1
2212 </dl>)html" };
2213 QVariantMap dict;
2214 if ( values.size() == 1 )
2215 {
2216 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2217 }
2218 else
2219 {
2220 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2221 return QVariant();
2222 }
2223
2224 if ( dict.isEmpty() )
2225 {
2226 return QVariant();
2227 }
2228
2229 QString rows;
2230
2231 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2232 {
2233 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2234 }
2235
2236 return table.arg( rows );
2237}
2238
2239static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2240{
2241 QVariant layer;
2242 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2243 {
2244 layer = context->variable( u"layer"_s );
2245 }
2246 else
2247 {
2248 //first node is layer id or name
2249 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2251 layer = node->eval( parent, context );
2253 }
2254
2255 QgsFeature feature;
2256 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2257 {
2258 feature = context->feature();
2259 }
2260 else
2261 {
2262 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2263 }
2264
2266 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2267 if ( strength == "hard"_L1 )
2268 {
2270 }
2271 else if ( strength == "soft"_L1 )
2272 {
2274 }
2275
2276 bool foundLayer = false;
2277 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2278 layer,
2279 context,
2280 parent,
2281 [parent, feature, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2282 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2283 if ( !layer )
2284 {
2285 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2286 return QVariant();
2287 }
2288
2289 const QgsFields fields = layer->fields();
2290 bool valid = true;
2291 for ( int i = 0; i < fields.size(); i++ )
2292 {
2293 QStringList errors;
2294 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2295 if ( !valid )
2296 {
2297 break;
2298 }
2299 }
2300
2301 return valid;
2302 },
2303 foundLayer
2304 );
2305
2306 if ( !foundLayer )
2307 {
2308 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2309 return QVariant();
2310 }
2311
2312 return res;
2313}
2314
2315static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2316{
2317 QVariant layer;
2318 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2319 {
2320 layer = context->variable( u"layer"_s );
2321 }
2322 else
2323 {
2324 //first node is layer id or name
2325 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2327 layer = node->eval( parent, context );
2329 }
2330
2331 QgsFeature feature;
2332 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2333 {
2334 feature = context->feature();
2335 }
2336 else
2337 {
2338 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2339 }
2340
2342 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2343 if ( strength == "hard"_L1 )
2344 {
2346 }
2347 else if ( strength == "soft"_L1 )
2348 {
2350 }
2351
2352 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2353
2354 bool foundLayer = false;
2355 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2356 layer,
2357 context,
2358 parent,
2359 [parent, feature, attributeName, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2360 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2361 if ( !layer )
2362 {
2363 return QVariant();
2364 }
2365
2366 const int fieldIndex = layer->fields().indexFromName( attributeName );
2367 if ( fieldIndex == -1 )
2368 {
2369 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2370 return QVariant();
2371 }
2372
2373 QStringList errors;
2374 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2375 return valid;
2376 },
2377 foundLayer
2378 );
2379
2380 if ( !foundLayer )
2381 {
2382 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2383 return QVariant();
2384 }
2385
2386 return res;
2387}
2388
2389static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2390{
2391 QgsFeature feature;
2392 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2393 {
2394 feature = context->feature();
2395 }
2396 else
2397 {
2398 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2399 }
2400
2401 const QgsFields fields = feature.fields();
2402 QVariantMap result;
2403 for ( int i = 0; i < fields.count(); ++i )
2404 {
2405 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2406 }
2407 return result;
2408}
2409
2410static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2411{
2412 QgsVectorLayer *layer = nullptr;
2413 QgsFeature feature;
2414
2415 // TODO this expression function is NOT thread safe
2417 if ( values.isEmpty() )
2418 {
2419 feature = context->feature();
2420 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2421 }
2422 else if ( values.size() == 1 )
2423 {
2424 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2425 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2426 }
2427 else if ( values.size() == 2 )
2428 {
2429 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2430 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2431 }
2432 else
2433 {
2434 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2435 return QVariant();
2436 }
2438
2439 if ( !layer )
2440 {
2441 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2442 return QVariant();
2443 }
2444
2445 if ( !feature.isValid() )
2446 {
2447 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2448 return QVariant();
2449 }
2450
2451 const QgsFields fields = feature.fields();
2452 QVariantMap result;
2453 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2454 {
2455 const QString fieldName { fields.at( fieldIndex ).name() };
2456 const QVariant attributeVal = feature.attribute( fieldIndex );
2457 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2458 if ( context && context->hasCachedValue( cacheValueKey ) )
2459 {
2460 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2461 }
2462 else
2463 {
2464 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2466 QVariant cache;
2467 if ( context )
2468 {
2469 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2470
2471 if ( !context->hasCachedValue( cacheKey ) )
2472 {
2473 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2474 context->setCachedValue( cacheKey, cache );
2475 }
2476 else
2477 {
2478 cache = context->cachedValue( cacheKey );
2479 }
2480 }
2481 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2482
2483 result.insert( fields.at( fieldIndex ).name(), value );
2484
2485 if ( context )
2486 {
2487 context->setCachedValue( cacheValueKey, value );
2488 }
2489 }
2490 }
2491 return result;
2492}
2493
2494static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2495{
2496 QgsVectorLayer *layer = nullptr;
2497 QgsFeature feature;
2498 bool evaluate = true;
2499
2500 // TODO this expression function is NOT thread safe
2502 if ( values.isEmpty() )
2503 {
2504 feature = context->feature();
2505 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2506 }
2507 else if ( values.size() == 1 )
2508 {
2509 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2510 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2511 }
2512 else if ( values.size() == 2 )
2513 {
2514 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2515 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2516 }
2517 else if ( values.size() == 3 )
2518 {
2519 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2520 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2521 evaluate = values.value( 2 ).toBool();
2522 }
2523 else
2524 {
2525 if ( isMaptip )
2526 {
2527 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2528 }
2529 else
2530 {
2531 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2532 }
2533 return QVariant();
2534 }
2535
2536 if ( !layer )
2537 {
2538 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2539 return QVariant();
2540 }
2542
2543 if ( !feature.isValid() )
2544 {
2545 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2546 return QVariant();
2547 }
2548
2549 if ( !evaluate )
2550 {
2551 if ( isMaptip )
2552 {
2553 return layer->mapTipTemplate();
2554 }
2555 else
2556 {
2557 return layer->displayExpression();
2558 }
2559 }
2560
2561 QgsExpressionContext subContext( *context );
2562 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2563 subContext.setFeature( feature );
2564
2565 if ( isMaptip )
2566 {
2567 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2568 }
2569 else
2570 {
2571 QgsExpression exp( layer->displayExpression() );
2572 exp.prepare( &subContext );
2573 return exp.evaluate( &subContext ).toString();
2574 }
2575}
2576
2577static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2578{
2579 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2580}
2581
2582static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2583{
2584 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2585}
2586
2587static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2588{
2589 QgsFeature feature;
2590 QVariant layer;
2591 if ( values.isEmpty() )
2592 {
2593 feature = context->feature();
2594 layer = context->variable( u"layer"_s );
2595 }
2596 else if ( values.size() == 1 )
2597 {
2598 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2599 layer = context->variable( u"layer"_s );
2600 }
2601 else if ( values.size() == 2 )
2602 {
2603 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2604 layer = values.at( 0 );
2605 }
2606 else
2607 {
2608 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2609 return QVariant();
2610 }
2611
2612 bool foundLayer = false;
2613 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2614 layer,
2615 context,
2616 parent,
2617 [feature]( QgsMapLayer *mapLayer ) -> QVariant {
2618 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2619 if ( !layer || !feature.isValid() )
2620 {
2621 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2622 }
2623
2624 return layer->selectedFeatureIds().contains( feature.id() );
2625 },
2626 foundLayer
2627 );
2628 if ( !foundLayer )
2629 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2630 else
2631 return res;
2632}
2633
2634static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2635{
2636 QVariant layer;
2637
2638 if ( values.isEmpty() )
2639 layer = context->variable( u"layer"_s );
2640 else if ( values.count() == 1 )
2641 layer = values.at( 0 );
2642 else
2643 {
2644 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2645 return QVariant();
2646 }
2647
2648 bool foundLayer = false;
2649 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2650 layer,
2651 context,
2652 parent,
2653 []( QgsMapLayer *mapLayer ) -> QVariant {
2654 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2655 if ( !layer )
2656 {
2657 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2658 }
2659
2660 return layer->selectedFeatureCount();
2661 },
2662 foundLayer
2663 );
2664 if ( !foundLayer )
2665 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2666 else
2667 return res;
2668}
2669
2670static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2671{
2672 static QMap<QString, qlonglong> counterCache;
2673 QVariant functionResult;
2674
2675 auto fetchAndIncrementFunc = [values, parent, &functionResult]( QgsMapLayer *mapLayer, const QString &databaseArgument ) {
2676 QString database;
2677
2678 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2679
2680 if ( layer )
2681 {
2682 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2683 database = decodedUri.value( u"path"_s ).toString();
2684 if ( database.isEmpty() )
2685 {
2686 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2687 }
2688 }
2689 else
2690 {
2691 database = databaseArgument;
2692 }
2693
2694 const QString table = values.at( 1 ).toString();
2695 const QString idColumn = values.at( 2 ).toString();
2696 const QString filterAttribute = values.at( 3 ).toString();
2697 const QVariant filterValue = values.at( 4 ).toString();
2698 const QVariantMap defaultValues = values.at( 5 ).toMap();
2699
2700 // read from database
2702 sqlite3_statement_unique_ptr sqliteStatement;
2703
2704 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2705 {
2706 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2707 functionResult = QVariant();
2708 return;
2709 }
2710
2711 QString errorMessage;
2712 QString currentValSql;
2713
2714 qlonglong nextId = 0;
2715 bool cachedMode = false;
2716 bool valueRetrieved = false;
2717
2718 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2719
2720 // Running in transaction mode, check for cached value first
2721 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2722 {
2723 cachedMode = true;
2724
2725 auto cachedCounter = counterCache.find( cacheString );
2726
2727 if ( cachedCounter != counterCache.end() )
2728 {
2729 qlonglong &cachedValue = cachedCounter.value();
2730 nextId = cachedValue;
2731 nextId += 1;
2732 cachedValue = nextId;
2733 valueRetrieved = true;
2734 }
2735 }
2736
2737 // Either not in cached mode or no cached value found, obtain from DB
2738 if ( !cachedMode || !valueRetrieved )
2739 {
2740 int result = SQLITE_ERROR;
2741
2742 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2743 if ( !filterAttribute.isNull() )
2744 {
2745 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2746 }
2747
2748 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2749
2750 if ( result == SQLITE_OK )
2751 {
2752 nextId = 0;
2753 if ( sqliteStatement.step() == SQLITE_ROW )
2754 {
2755 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2756 }
2757
2758 // If in cached mode: add value to cache and connect to transaction
2759 if ( cachedMode && result == SQLITE_OK )
2760 {
2761 counterCache.insert( cacheString, nextId );
2762
2763 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() { counterCache.remove( cacheString ); } );
2764 }
2765 valueRetrieved = true;
2766 }
2767 }
2768
2769 if ( valueRetrieved )
2770 {
2771 QString upsertSql;
2772 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2773 QStringList cols;
2774 QStringList vals;
2775 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2776 vals << QgsSqliteUtils::quotedValue( nextId );
2777
2778 if ( !filterAttribute.isNull() )
2779 {
2780 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2781 vals << QgsSqliteUtils::quotedValue( filterValue );
2782 }
2783
2784 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2785 {
2786 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2787 vals << iter.value().toString();
2788 }
2789
2790 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2791 upsertSql += " VALUES "_L1;
2792 upsertSql += '(' + vals.join( ',' ) + ')';
2793
2794 int result = SQLITE_ERROR;
2795 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2796 {
2797 QgsTransaction *transaction = layer->dataProvider()->transaction();
2798 if ( transaction->executeSql( upsertSql, errorMessage ) )
2799 {
2800 result = SQLITE_OK;
2801 }
2802 }
2803 else
2804 {
2805 result = sqliteDb.exec( upsertSql, errorMessage );
2806 }
2807 if ( result == SQLITE_OK )
2808 {
2809 functionResult = QVariant( nextId );
2810 return;
2811 }
2812 else
2813 {
2814 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2815 functionResult = QVariant();
2816 return;
2817 }
2818 }
2819
2820 functionResult = QVariant();
2821 };
2822
2823 bool foundLayer = false;
2824 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer *layer ) { fetchAndIncrementFunc( layer, QString() ); }, foundLayer );
2825 if ( !foundLayer )
2826 {
2827 const QString databasePath = values.at( 0 ).toString();
2828 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath] { fetchAndIncrementFunc( nullptr, databasePath ); } );
2829 }
2830
2831 return functionResult;
2832}
2833
2834static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2835{
2836 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2837 if ( !crs.isValid() )
2838 return QVariant();
2839 return crs.authid();
2840}
2841
2842static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2843{
2844 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2845 QgsCoordinateReferenceSystem crs( definition );
2846
2847 if ( !crs.isValid() )
2848 {
2849 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2850 }
2851
2852 return QVariant::fromValue( crs );
2853}
2854
2855static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2856{
2857 QString concat( "" );
2858 for ( const QVariant &value : values )
2859 {
2860 if ( !QgsVariantUtils::isNull( value ) )
2861 concat += QgsExpressionUtils::getStringValue( value, parent );
2862 }
2863 return concat;
2864}
2865
2866static QVariant fcnConcatWs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2867{
2868 if ( values.length() < 2 )
2869 {
2870 parent->setEvalErrorString( QObject::tr( "Function concat_ws requires at least 2 arguments" ) );
2871 return QVariant();
2872 }
2873
2874 const QString separator = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2875
2876 QStringList stringValues;
2877 stringValues.reserve( values.size() - 1 );
2878 for ( int i = 1; i < values.size(); ++i )
2879 {
2880 const QVariant value = values.at( i );
2881 if ( !QgsVariantUtils::isNull( value ) )
2882 {
2883 stringValues.append( QgsExpressionUtils::getStringValue( value, parent ) );
2884 }
2885 }
2886
2887 return stringValues.join( separator );
2888}
2889
2890static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2891{
2892 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2893 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2894}
2895
2896static QVariant fcnUnaccent( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction *node )
2897{
2898 Q_UNUSED( context )
2899 Q_UNUSED( node )
2900
2901 if ( values.isEmpty() || values[0].isNull() )
2902 return QVariant();
2903
2904 return QgsStringUtils::unaccent( values[0].toString() );
2905}
2906
2907
2908static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2909{
2910 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2911 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2912 return string.right( pos );
2913}
2914
2915static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2916{
2917 if ( values.length() < 2 || values.length() > 3 )
2918 return QVariant();
2919
2920 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2921 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2922
2923 bool overlapping = false;
2924 if ( values.length() == 3 )
2925 {
2926 overlapping = values.at( 2 ).toBool();
2927 }
2928
2929 if ( substring.isEmpty() )
2930 return QVariant( 0 );
2931
2932 int count = 0;
2933 if ( overlapping )
2934 {
2935 count = input.count( substring );
2936 }
2937 else
2938 {
2939 int pos = 0;
2940 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2941 {
2942 count++;
2943 pos += substring.length();
2944 }
2945 }
2946
2947 return QVariant( count );
2948}
2949
2950static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2951{
2952 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2953 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2954 return string.left( pos );
2955}
2956
2957static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2958{
2959 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2960 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2961 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2962 return string.leftJustified( length, fill.at( 0 ), true );
2963}
2964
2965static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2966{
2967 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2968 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2969 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2970 return string.rightJustified( length, fill.at( 0 ), true );
2971}
2972
2973static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2974{
2975 if ( values.size() < 1 )
2976 {
2977 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2978 return QVariant();
2979 }
2980
2981 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2982 for ( int n = 1; n < values.length(); n++ )
2983 {
2984 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2985 }
2986 return string;
2987}
2988
2989
2990static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2991{
2992 return QVariant( QDateTime::currentDateTime() );
2993}
2994
2995static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2996{
2997 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2998 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2999 if ( format.isEmpty() && !language.isEmpty() )
3000 {
3001 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
3002 return QVariant( QDate() );
3003 }
3004
3005 if ( format.isEmpty() && language.isEmpty() )
3006 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
3007
3008 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3009 QLocale locale = QLocale();
3010 if ( !language.isEmpty() )
3011 {
3012 locale = QLocale( language );
3013 }
3014
3015 QDate date = locale.toDate( datestring, format );
3016 if ( !date.isValid() )
3017 {
3018 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
3019 date = QDate();
3020 }
3021 return QVariant( date );
3022}
3023
3024static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3025{
3026 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3027 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3028 if ( format.isEmpty() && !language.isEmpty() )
3029 {
3030 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
3031 return QVariant( QTime() );
3032 }
3033
3034 if ( format.isEmpty() && language.isEmpty() )
3035 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
3036
3037 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3038 QLocale locale = QLocale();
3039 if ( !language.isEmpty() )
3040 {
3041 locale = QLocale( language );
3042 }
3043
3044 QTime time = locale.toTime( timestring, format );
3045 if ( !time.isValid() )
3046 {
3047 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
3048 time = QTime();
3049 }
3050 return QVariant( time );
3051}
3052
3053static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3054{
3055 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
3056}
3057
3058/*
3059 * DMS functions
3060 */
3061
3062static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3063{
3064 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3065 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3066 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3067
3068 QString formatString;
3069 if ( values.count() > 3 )
3070 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
3071
3073 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
3074 {
3076 }
3077 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
3078 {
3080 }
3081 else if ( !formatString.isEmpty() )
3082 {
3083 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
3084 return QVariant();
3085 }
3086
3087 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
3088 {
3089 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
3090 }
3091 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
3092 {
3093 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
3094 }
3095 else
3096 {
3097 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
3098 return QVariant();
3099 }
3100}
3101
3102static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3103{
3105 return floatToDegreeFormat( format, values, context, parent, node );
3106}
3107
3108static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3109{
3110 double value = 0.0;
3111 bool ok = false;
3112 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
3113
3114 return ok ? QVariant( value ) : QVariant();
3115}
3116
3117static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3118{
3120 return floatToDegreeFormat( format, values, context, parent, node );
3121}
3122
3123static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3124{
3125 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3126 return static_cast< int >( decimalDegrees );
3127}
3128
3129static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3130{
3131 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3132 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3133 return static_cast< int >( remainder * 60 );
3134}
3135
3136static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3137{
3138 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3139 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3140 const double remainderInMinutes = remainder * 60;
3141 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
3142 // do not truncate to int, this function returns decimal seconds!
3143 return remainderSecondsFraction * 60;
3144}
3145
3146static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3147{
3148 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3149 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3150 qint64 seconds = d2.secsTo( d1 );
3151 return QVariant::fromValue( QgsInterval( seconds ) );
3152}
3153
3154static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3155{
3156 if ( !values.at( 0 ).canConvert<QDate>() )
3157 return QVariant();
3158
3159 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
3160 if ( !date.isValid() )
3161 return QVariant();
3162
3163 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
3164 // (to match PostgreSQL behavior)
3165 return date.dayOfWeek() % 7;
3166}
3167
3168static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3169{
3170 QVariant value = values.at( 0 );
3171 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3172 if ( inter.isValid() )
3173 {
3174 return QVariant( inter.days() );
3175 }
3176 else
3177 {
3178 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3179 return QVariant( d1.date().day() );
3180 }
3181}
3182
3183static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3184{
3185 QVariant value = values.at( 0 );
3186 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3187 if ( inter.isValid() )
3188 {
3189 return QVariant( inter.years() );
3190 }
3191 else
3192 {
3193 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3194 return QVariant( d1.date().year() );
3195 }
3196}
3197
3198static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3199{
3200 QVariant value = values.at( 0 );
3201 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3202 if ( inter.isValid() )
3203 {
3204 return QVariant( inter.months() );
3205 }
3206 else
3207 {
3208 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3209 return QVariant( d1.date().month() );
3210 }
3211}
3212
3213static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3214{
3215 QVariant value = values.at( 0 );
3216 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3217 if ( inter.isValid() )
3218 {
3219 return QVariant( inter.weeks() );
3220 }
3221 else
3222 {
3223 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3224 return QVariant( d1.date().weekNumber() );
3225 }
3226}
3227
3228static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3229{
3230 QVariant value = values.at( 0 );
3231 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3232 if ( inter.isValid() )
3233 {
3234 return QVariant( inter.hours() );
3235 }
3236 else
3237 {
3238 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3239 return QVariant( t1.hour() );
3240 }
3241}
3242
3243static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3244{
3245 QVariant value = values.at( 0 );
3246 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3247 if ( inter.isValid() )
3248 {
3249 return QVariant( inter.minutes() );
3250 }
3251 else
3252 {
3253 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3254 return QVariant( t1.minute() );
3255 }
3256}
3257
3258static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3259{
3260 QVariant value = values.at( 0 );
3261 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3262 if ( inter.isValid() )
3263 {
3264 return QVariant( inter.seconds() );
3265 }
3266 else
3267 {
3268 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3269 return QVariant( t1.second() );
3270 }
3271}
3272
3273static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3274{
3275 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3276 if ( dt.isValid() )
3277 {
3278 return QVariant( dt.toMSecsSinceEpoch() );
3279 }
3280 else
3281 {
3282 return QVariant();
3283 }
3284}
3285
3286static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3287{
3288 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3289 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3290 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3291}
3292
3293static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3294{
3295 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3296 if ( parent->hasEvalError() )
3297 {
3298 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3299 return QVariant();
3300 }
3301 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3302 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3303}
3304
3305static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3307 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3308 if ( parent->hasEvalError() )
3309 {
3310 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3311 return QVariant();
3312 }
3313 bool ok;
3314 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3315}
3316
3317double qDateTimeToDecimalYear( const QDateTime &dateTime )
3318{
3319 if ( !dateTime.isValid() )
3320 {
3321 return 0.0;
3322 }
3323
3324 const int year = dateTime.date().year();
3325 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3326 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3327 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3328 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3329 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3330}
3331
3332static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3333{
3334 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3335 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3336 if ( parent->hasEvalError() )
3337 {
3338 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3339 return QVariant();
3340 }
3341 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3342 if ( parent->hasEvalError() )
3343 {
3344 return QVariant();
3345 }
3346 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3347 if ( parent->hasEvalError() )
3348 {
3349 return QVariant();
3350 }
3351 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3352 if ( parent->hasEvalError() )
3353 {
3354 return QVariant();
3355 }
3356 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3357
3358 const QgsMagneticModel model( name, filePath );
3359 try
3360 {
3361 double declination = 0;
3362 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3363 {
3364 return declination;
3365 }
3366 else
3367 {
3368 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3369 }
3370 }
3371 catch ( QgsNotSupportedException &e )
3372 {
3373 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3374 }
3375 return QVariant();
3376}
3377
3378static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3379{
3380 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3381 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3382 if ( parent->hasEvalError() )
3383 {
3384 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3385 return QVariant();
3386 }
3387 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3388 if ( parent->hasEvalError() )
3389 {
3390 return QVariant();
3391 }
3392 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3393 if ( parent->hasEvalError() )
3394 {
3395 return QVariant();
3396 }
3397 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3398 if ( parent->hasEvalError() )
3399 {
3400 return QVariant();
3401 }
3402 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3403
3404 const QgsMagneticModel model( name, filePath );
3405 try
3406 {
3407 double inclination = 0;
3408 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3409 {
3410 return inclination;
3411 }
3412 else
3413 {
3414 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3415 }
3416 }
3417 catch ( QgsNotSupportedException &e )
3418 {
3419 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3420 }
3421 return QVariant();
3422}
3423
3424static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3425{
3426 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3427 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3428 if ( parent->hasEvalError() )
3429 {
3430 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3431 return QVariant();
3432 }
3433 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3434 if ( parent->hasEvalError() )
3435 {
3436 return QVariant();
3437 }
3438 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3439 if ( parent->hasEvalError() )
3440 {
3441 return QVariant();
3442 }
3443 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3444 if ( parent->hasEvalError() )
3445 {
3446 return QVariant();
3447 }
3448 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3449
3450 const QgsMagneticModel model( name, filePath );
3451 try
3452 {
3453 double declination = 0;
3454 double Bx = 0;
3455 double By = 0;
3456 double Bz = 0;
3457 double Bxt = 0;
3458 double Byt = 0;
3459 double Bzt = 0;
3460
3461 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3462 {
3463 double H = 0;
3464 double F = 0;
3465 double D = 0;
3466 double I = 0;
3467 double Ht = 0;
3468 double Ft = 0;
3469 double Dt = 0;
3470 double It = 0;
3471 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3472 {
3473 return Dt;
3474 }
3475 else
3476 {
3477 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3478 }
3479 return declination;
3480 }
3481 else
3482 {
3483 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3484 }
3485 }
3486 catch ( QgsNotSupportedException &e )
3487 {
3488 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3489 }
3490 return QVariant();
3491}
3492
3493static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3494{
3495 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3496 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3497 if ( parent->hasEvalError() )
3498 {
3499 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3500 return QVariant();
3501 }
3502 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3503 if ( parent->hasEvalError() )
3504 {
3505 return QVariant();
3506 }
3507 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3508 if ( parent->hasEvalError() )
3509 {
3510 return QVariant();
3511 }
3512 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3513 if ( parent->hasEvalError() )
3514 {
3515 return QVariant();
3516 }
3517 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3518
3519 const QgsMagneticModel model( name, filePath );
3520 try
3521 {
3522 double declination = 0;
3523 double Bx = 0;
3524 double By = 0;
3525 double Bz = 0;
3526 double Bxt = 0;
3527 double Byt = 0;
3528 double Bzt = 0;
3529
3530 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3531 {
3532 double H = 0;
3533 double F = 0;
3534 double D = 0;
3535 double I = 0;
3536 double Ht = 0;
3537 double Ft = 0;
3538 double Dt = 0;
3539 double It = 0;
3540 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3541 {
3542 return It;
3543 }
3544 else
3545 {
3546 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3547 }
3548 return declination;
3549 }
3550 else
3552 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3553 }
3554 }
3555 catch ( QgsNotSupportedException &e )
3556 {
3557 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3558 }
3559 return QVariant();
3560}
3561
3562#define ENSURE_GEOM_TYPE( f, g, geomtype ) \
3563 if ( !( f ).hasGeometry() ) \
3564 return QVariant(); \
3565 QgsGeometry g = ( f ).geometry(); \
3566 if ( ( g ).type() != ( geomtype ) ) \
3567 return QVariant();
3568
3569static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3570{
3571 FEAT_FROM_CONTEXT( context, f )
3573 if ( g.isMultipart() )
3574 {
3575 return g.asMultiPoint().at( 0 ).x();
3576 }
3577 else
3578 {
3579 return g.asPoint().x();
3580 }
3581}
3582
3583static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3584{
3585 FEAT_FROM_CONTEXT( context, f )
3587 if ( g.isMultipart() )
3588 {
3589 return g.asMultiPoint().at( 0 ).y();
3590 }
3591 else
3592 {
3593 return g.asPoint().y();
3594 }
3595}
3596
3597static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3598{
3599 FEAT_FROM_CONTEXT( context, f )
3601
3602 if ( g.isEmpty() )
3603 return QVariant();
3604
3605 const QgsAbstractGeometry *abGeom = g.constGet();
3606
3607 if ( g.isEmpty() || !abGeom->is3D() )
3608 return QVariant();
3609
3610 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3611 {
3612 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3613 if ( point )
3614 return point->z();
3615 }
3616 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3617 {
3618 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3619 {
3620 if ( collection->numGeometries() > 0 )
3621 {
3622 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3623 return point->z();
3624 }
3625 }
3626 }
3627
3628 return QVariant();
3629}
3630
3631static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3632{
3633 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3634 if ( geom.isNull() )
3635 return QVariant();
3636
3637 bool isValid = geom.isGeosValid();
3638
3639 return QVariant( isValid );
3640}
3641
3642static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3643{
3644 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3645 if ( geom.isNull() )
3646 return QVariant();
3647
3648 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3649#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
3651#else
3653#endif
3654 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3656 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3658
3659 const bool keepCollapsed = values.value( 2 ).toBool();
3660
3661 QgsGeometry valid;
3662 try
3663 {
3664 valid = geom.makeValid( method, keepCollapsed );
3665 }
3666 catch ( QgsNotSupportedException & )
3667 {
3668 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3669 return QVariant();
3670 }
3671
3672 return QVariant::fromValue( valid );
3673}
3674
3675static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3676{
3677 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3678 if ( geom.isNull() )
3679 return QVariant();
3680
3681 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3682 QVariantList array;
3683 for ( int i = 0; i < multiGeom.size(); ++i )
3684 {
3685 array += QVariant::fromValue( multiGeom.at( i ) );
3686 }
3687
3688 return array;
3689}
3690
3691static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3692{
3693 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3694 if ( geom.isNull() )
3695 return QVariant();
3696
3697 //if single point, return the point's x coordinate
3698 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3699 {
3700 return geom.asPoint().x();
3701 }
3702
3703 //otherwise return centroid x
3704 QgsGeometry centroid = geom.centroid();
3705 QVariant result( centroid.asPoint().x() );
3706 return result;
3707}
3708
3709static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3710{
3711 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3712 if ( geom.isNull() )
3713 return QVariant();
3714
3715 //if single point, return the point's y coordinate
3716 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3717 {
3718 return geom.asPoint().y();
3719 }
3720
3721 //otherwise return centroid y
3722 QgsGeometry centroid = geom.centroid();
3723 QVariant result( centroid.asPoint().y() );
3724 return result;
3725}
3726
3727static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3728{
3729 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3730 if ( geom.isNull() )
3731 return QVariant(); //or 0?
3732
3733 if ( !geom.constGet()->is3D() )
3734 return QVariant();
3735
3736 //if single point, return the point's z coordinate
3737 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3738 {
3740 if ( point )
3741 return point->z();
3742 }
3743 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3744 {
3746 {
3747 if ( collection->numGeometries() == 1 )
3748 {
3749 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3750 return point->z();
3751 }
3752 }
3753 }
3754
3755 return QVariant();
3756}
3757
3758static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3759{
3760 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3761 if ( geom.isNull() )
3762 return QVariant(); //or 0?
3763
3764 if ( !geom.constGet()->isMeasure() )
3765 return QVariant();
3766
3767 //if single point, return the point's m value
3768 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3769 {
3771 if ( point )
3772 return point->m();
3773 }
3774 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3775 {
3777 {
3778 if ( collection->numGeometries() == 1 )
3779 {
3780 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3781 return point->m();
3782 }
3783 }
3784 }
3785
3786 return QVariant();
3787}
3788
3789static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3790{
3791 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3792
3793 if ( geom.isNull() )
3794 return QVariant();
3795
3796 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3797
3798 if ( idx < 0 )
3799 {
3800 //negative idx
3801 int count = geom.constGet()->nCoordinates();
3802 idx = count + idx;
3803 }
3804 else
3805 {
3806 //positive idx is 1 based
3807 idx -= 1;
3808 }
3809
3810 QgsVertexId vId;
3811 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3812 {
3813 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3814 return QVariant();
3815 }
3816
3817 QgsPoint point = geom.constGet()->vertexAt( vId );
3818 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3819}
3820
3821static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3822{
3823 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3824
3825 if ( geom.isNull() )
3826 return QVariant();
3827
3828 QgsVertexId vId;
3829 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3830 {
3831 return QVariant();
3832 }
3833
3834 QgsPoint point = geom.constGet()->vertexAt( vId );
3835 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3836}
3837
3838static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3839{
3840 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3841
3842 if ( geom.isNull() )
3843 return QVariant();
3844
3845 QgsVertexId vId;
3846 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3847 {
3848 return QVariant();
3849 }
3850
3851 QgsPoint point = geom.constGet()->vertexAt( vId );
3852 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3853}
3854
3855static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3856{
3857 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3858
3859 if ( geom.isNull() )
3860 return QVariant();
3861
3862 bool ignoreClosing = false;
3863 if ( values.length() > 1 )
3864 {
3865 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3866 }
3867
3868 QgsMultiPoint *mp = new QgsMultiPoint();
3869
3870 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3871 for ( const QgsRingSequence &part : sequence )
3872 {
3873 for ( const QgsPointSequence &ring : part )
3874 {
3875 bool skipLast = false;
3876 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3877 {
3878 skipLast = true;
3879 }
3880
3881 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++i )
3882 {
3883 mp->addGeometry( ring.at( i ).clone() );
3884 }
3885 }
3886 }
3887
3888 return QVariant::fromValue( QgsGeometry( mp ) );
3889}
3890
3891static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3892{
3893 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3894
3895 if ( geom.isNull() )
3896 return QVariant();
3897
3898 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3899
3900 //OK, now we have a complete list of segmentized lines from the geometry
3902 for ( QgsLineString *line : linesToProcess )
3903 {
3904 for ( int i = 0; i < line->numPoints() - 1; ++i )
3905 {
3907 segment->setPoints( QgsPointSequence() << line->pointN( i ) << line->pointN( i + 1 ) );
3908 ml->addGeometry( segment );
3909 }
3910 delete line;
3911 }
3912
3913 return QVariant::fromValue( QgsGeometry( ml ) );
3914}
3915
3916static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3917{
3918 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3919
3920 if ( geom.isNull() )
3921 return QVariant();
3922
3924 if ( !curvePolygon && geom.isMultipart() )
3925 {
3927 {
3928 if ( collection->numGeometries() == 1 )
3929 {
3930 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3931 }
3932 }
3933 }
3934
3935 if ( !curvePolygon )
3936 return QVariant();
3937
3938 //idx is 1 based
3939 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3940
3941 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3942 return QVariant();
3943
3944 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3945 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3946 return result;
3947}
3948
3949static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3950{
3951 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3952
3953 if ( geom.isNull() )
3954 return QVariant();
3955
3957 if ( !collection )
3958 return QVariant();
3959
3960 //idx is 1 based
3961 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3962
3963 if ( idx < 0 || idx >= collection->numGeometries() )
3964 return QVariant();
3965
3966 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3967 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3968 return result;
3969}
3970
3971static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3972{
3973 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3974
3975 if ( geom.isNull() )
3976 return QVariant();
3977
3978 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3979 if ( !boundary )
3980 return QVariant();
3981
3982 return QVariant::fromValue( QgsGeometry( boundary ) );
3983}
3984
3985static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3986{
3987 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3988
3989 if ( geom.isNull() )
3990 return QVariant();
3991
3992 QgsGeometry merged = geom.mergeLines();
3993 if ( merged.isNull() )
3994 return QVariant();
3995
3996 return QVariant::fromValue( merged );
3997}
3998
3999static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4000{
4001 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4002 if ( geom.isNull() )
4003 return QVariant();
4004
4005 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4006 if ( geom2.isNull() )
4007 return QVariant();
4008
4009 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
4010 if ( sharedPaths.isNull() )
4011 return QVariant();
4012
4013 return QVariant::fromValue( sharedPaths );
4014}
4015
4016
4017static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4018{
4019 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4020
4021 if ( geom.isNull() )
4022 return QVariant();
4023
4024 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4025
4026 QgsGeometry simplified = geom.simplify( tolerance );
4027 if ( simplified.isNull() )
4028 return QVariant();
4029
4030 return simplified;
4031}
4032
4033static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4034{
4035 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4036
4037 if ( geom.isNull() )
4038 return QVariant();
4039
4040 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4041
4043
4044 QgsGeometry simplified = simplifier.simplify( geom );
4045 if ( simplified.isNull() )
4046 return QVariant();
4047
4048 return simplified;
4049}
4050
4051static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4052{
4053 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4054
4055 if ( geom.isNull() )
4056 return QVariant();
4057
4058 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
4059 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
4060 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4061 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
4062
4063 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
4064 if ( smoothed.isNull() )
4065 return QVariant();
4066
4067 return smoothed;
4068}
4069
4070static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4071{
4072 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4073
4074 if ( geom.isNull() )
4075 return QVariant();
4076
4077 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4078 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4079 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4080
4081 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
4082 if ( waved.isNull() )
4083 return QVariant();
4084
4085 return waved;
4086}
4087
4088static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4089{
4090 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4091
4092 if ( geom.isNull() )
4093 return QVariant();
4094
4095 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4096 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4097 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4098 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4099 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4100
4101 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4102 if ( waved.isNull() )
4103 return QVariant();
4104
4105 return waved;
4106}
4107
4108static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4109{
4110 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4111
4112 if ( geom.isNull() )
4113 return QVariant();
4114
4115 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4116 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4117 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4118
4119 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
4120 if ( waved.isNull() )
4121 return QVariant();
4122
4123 return waved;
4124}
4125
4126static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4127{
4128 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4129
4130 if ( geom.isNull() )
4131 return QVariant();
4132
4133 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4134 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4135 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4136 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4137 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4138
4139 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4140 if ( waved.isNull() )
4141 return QVariant();
4142
4143 return waved;
4144}
4145
4146static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4147{
4148 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4149
4150 if ( geom.isNull() )
4151 return QVariant();
4152
4153 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4154 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4155 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4156
4157 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
4158 if ( waved.isNull() )
4159 return QVariant();
4160
4161 return waved;
4162}
4163
4164static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4165{
4166 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4167
4168 if ( geom.isNull() )
4169 return QVariant();
4170
4171 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4172 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4173 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4174 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4175 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4176
4177 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4178 if ( waved.isNull() )
4179 return QVariant();
4180
4181 return waved;
4182}
4183
4184static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4185{
4186 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4187
4188 if ( geom.isNull() )
4189 return QVariant();
4190
4191 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4192 QVector< double > dashPattern;
4193 dashPattern.reserve( pattern.size() );
4194 for ( const QVariant &value : std::as_const( pattern ) )
4195 {
4196 bool ok = false;
4197 double v = value.toDouble( &ok );
4198 if ( ok )
4199 {
4200 dashPattern << v;
4201 }
4202 else
4203 {
4204 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4205 return QgsGeometry();
4206 }
4207 }
4208
4209 if ( dashPattern.size() % 2 != 0 )
4210 {
4211 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4212 return QgsGeometry();
4213 }
4214
4215 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4217 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4219 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4221 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4223 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4225 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4227 else
4228 {
4229 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4230 return QgsGeometry();
4231 }
4232
4233 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4235 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4237 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4239 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4241 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4243 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4245 else
4246 {
4247 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4248 return QgsGeometry();
4249 }
4250
4251 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4253 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4255 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4257 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4259 else
4260 {
4261 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4262 return QgsGeometry();
4263 }
4264
4265 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4266
4267 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4268 if ( result.isNull() )
4269 return QVariant();
4270
4271 return result;
4272}
4273
4274static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4275{
4276 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4277
4278 if ( geom.isNull() )
4279 return QVariant();
4280
4281 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4282 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4283 if ( densified.isNull() )
4284 return QVariant();
4285
4286 return densified;
4287}
4288
4289static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4290{
4291 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4292
4293 if ( geom.isNull() )
4294 return QVariant();
4295
4296 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4297 const QgsGeometry densified = geom.densifyByDistance( distance );
4298 if ( densified.isNull() )
4299 return QVariant();
4300
4301 return densified;
4302}
4303
4304static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4305{
4306 QVariantList list;
4307 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4308 {
4309 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4310 }
4311 else
4312 {
4313 list = values;
4314 }
4315
4316 QVector< QgsGeometry > parts;
4317 parts.reserve( list.size() );
4318 for ( const QVariant &value : std::as_const( list ) )
4319 {
4320 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4321 if ( part.isNull() )
4322 return QgsGeometry();
4323 parts << part;
4324 }
4325
4326 return QgsGeometry::collectGeometry( parts );
4327}
4328
4329static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4330{
4331 if ( values.count() < 2 || values.count() > 4 )
4332 {
4333 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4334 return QVariant();
4335 }
4336
4337 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4338 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4339 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4340 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4341 switch ( values.count() )
4342 {
4343 case 2:
4344 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4345 case 3:
4346 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4347 case 4:
4348 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4349 }
4350 return QVariant(); //avoid warning
4351}
4352
4353static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4354{
4355 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4356 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4357 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4358 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4359}
4360
4361static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4362{
4363 if ( values.empty() )
4364 {
4365 return QVariant();
4366 }
4367
4368 QVector<QgsPoint> points;
4369 points.reserve( values.count() );
4370
4371 auto addPoint = [&points]( const QgsGeometry &geom ) {
4372 if ( geom.isNull() )
4373 return;
4374
4375 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4376 return;
4377
4379 if ( !point )
4380 return;
4381
4382 points << *point;
4383 };
4384
4385 for ( const QVariant &value : values )
4386 {
4387 if ( value.userType() == QMetaType::Type::QVariantList )
4388 {
4389 const QVariantList list = value.toList();
4390 for ( const QVariant &v : list )
4391 {
4392 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4393 }
4394 }
4395 else
4396 {
4397 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4398 }
4399 }
4400
4401 if ( points.count() < 2 )
4402 return QVariant();
4403
4404 return QgsGeometry( new QgsLineString( points ) );
4405}
4406
4407static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4408{
4409 if ( values.count() < 1 )
4410 {
4411 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4412 return QVariant();
4413 }
4414
4415 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4416
4417 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4418 return outerRing; // if it's already a polygon we have nothing to do
4419
4420 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4421 return QVariant();
4422
4423 auto polygon = std::make_unique< QgsPolygon >();
4424
4425 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4426 if ( !exteriorRing && outerRing.isMultipart() )
4427 {
4429 {
4430 if ( collection->numGeometries() == 1 )
4431 {
4432 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4433 }
4434 }
4435 }
4436
4437 if ( !exteriorRing )
4438 return QVariant();
4439
4440 polygon->setExteriorRing( exteriorRing->segmentize() );
4441
4442
4443 for ( int i = 1; i < values.count(); ++i )
4444 {
4445 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4446 if ( ringGeom.isNull() )
4447 continue;
4448
4449 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4450 continue;
4451
4452 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4453 if ( !ring && ringGeom.isMultipart() )
4454 {
4456 {
4457 if ( collection->numGeometries() == 1 )
4458 {
4459 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4460 }
4461 }
4462 }
4463
4464 if ( !ring )
4465 continue;
4466
4467 polygon->addInteriorRing( ring->segmentize() );
4468 }
4469
4470 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4471}
4472
4473static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4474{
4475 auto tr = std::make_unique<QgsTriangle>();
4476 auto lineString = std::make_unique<QgsLineString>();
4477 lineString->clear();
4478
4479 for ( const QVariant &value : values )
4480 {
4481 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4482 if ( geom.isNull() )
4483 return QVariant();
4484
4485 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4486 return QVariant();
4487
4489 if ( !point && geom.isMultipart() )
4490 {
4492 {
4493 if ( collection->numGeometries() == 1 )
4494 {
4495 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4496 }
4497 }
4498 }
4499
4500 if ( !point )
4501 return QVariant();
4502
4503 lineString->addVertex( *point );
4504 }
4505
4506 tr->setExteriorRing( lineString.release() );
4507
4508 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4509}
4510
4511static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4512{
4513 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4514 if ( geom.isNull() )
4515 return QVariant();
4516
4517 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4518 return QVariant();
4519
4520 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4521 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4522
4523 if ( segment < 3 )
4524 {
4525 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4526 return QVariant();
4527 }
4529 if ( !point && geom.isMultipart() )
4530 {
4532 {
4533 if ( collection->numGeometries() == 1 )
4534 {
4535 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4536 }
4537 }
4538 }
4539 if ( !point )
4540 return QVariant();
4541
4542 QgsCircle circ( *point, radius );
4543 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4544}
4545
4546static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4547{
4548 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4549 if ( geom.isNull() )
4550 return QVariant();
4551
4552 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4553 return QVariant();
4554
4555 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4556 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4557 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4558 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4559 if ( segment < 3 )
4560 {
4561 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4562 return QVariant();
4563 }
4565 if ( !point && geom.isMultipart() )
4566 {
4568 {
4569 if ( collection->numGeometries() == 1 )
4570 {
4571 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4572 }
4573 }
4574 }
4575 if ( !point )
4576 return QVariant();
4577
4578 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4579 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4580}
4581
4582static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4583{
4584 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4585 if ( pt1.isNull() )
4586 return QVariant();
4587
4588 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4589 return QVariant();
4590
4591 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4592 if ( pt2.isNull() )
4593 return QVariant();
4594
4595 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4596 return QVariant();
4597
4598 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4599 if ( nbEdges < 3 )
4600 {
4601 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4602 return QVariant();
4603 }
4604
4605 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4607 {
4608 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4609 return QVariant();
4610 }
4611
4613 if ( !center && pt1.isMultipart() )
4614 {
4616 {
4617 if ( collection->numGeometries() == 1 )
4618 {
4619 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4620 }
4621 }
4622 }
4623 if ( !center )
4624 return QVariant();
4625
4627 if ( !corner && pt2.isMultipart() )
4628 {
4630 {
4631 if ( collection->numGeometries() == 1 )
4632 {
4633 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4634 }
4635 }
4636 }
4637 if ( !corner )
4638 return QVariant();
4639
4640 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4641
4642 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4643}
4644
4645static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4646{
4647 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4648 if ( pt1.isNull() )
4649 return QVariant();
4650 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4651 return QVariant();
4652
4653 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4654 if ( pt2.isNull() )
4655 return QVariant();
4656 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4657 return QVariant();
4658
4659 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4660 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4661 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4662
4663 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4664}
4665
4666static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4667{
4668 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4669 if ( pt1.isNull() )
4670 return QVariant();
4671 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4672 return QVariant();
4673
4674 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4675 if ( pt2.isNull() )
4676 return QVariant();
4677 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4678 return QVariant();
4679
4680 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4681 if ( pt3.isNull() )
4682 return QVariant();
4683 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4684 return QVariant();
4685
4686 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4687 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4688 {
4689 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4690 return QVariant();
4691 }
4692 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4693 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4694 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4695 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4696 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4697}
4698
4699static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4700{
4701 if ( geom.isNull() )
4702 return QVariant();
4703
4704 if ( idx < 0 )
4705 {
4706 idx += geom.constGet()->nCoordinates();
4707 }
4708 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4709 {
4710 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4711 return QVariant();
4712 }
4713 return QVariant::fromValue( geom.vertexAt( idx ) );
4714}
4715
4716// function used for the old $ style
4717static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4718{
4719 FEAT_FROM_CONTEXT( context, feature )
4720 const QgsGeometry geom = feature.geometry();
4721 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4722
4723 const QVariant v = pointAt( geom, idx, parent );
4724
4725 if ( !v.isNull() )
4726 return QVariant( v.value<QgsPoint>().x() );
4727 else
4728 return QVariant();
4729}
4730static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4731{
4732 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))
4733 {
4734 return fcnOldXat( values, f, parent, node );
4735 }
4736 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)
4737 {
4738 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4739 }
4740
4741 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4742 if ( geom.isNull() )
4743 {
4744 return QVariant();
4745 }
4746
4747 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4748
4749 const QVariant v = pointAt( geom, vertexNumber, parent );
4750 if ( !v.isNull() )
4751 return QVariant( v.value<QgsPoint>().x() );
4752 else
4753 return QVariant();
4754}
4755
4756// function used for the old $ style
4757static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4758{
4759 FEAT_FROM_CONTEXT( context, feature )
4760 const QgsGeometry geom = feature.geometry();
4761 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4762
4763 const QVariant v = pointAt( geom, idx, parent );
4764
4765 if ( !v.isNull() )
4766 return QVariant( v.value<QgsPoint>().y() );
4767 else
4768 return QVariant();
4769}
4770static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4771{
4772 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))
4773 {
4774 return fcnOldYat( values, f, parent, node );
4775 }
4776 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)
4777 {
4778 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4779 }
4780
4781 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4782 if ( geom.isNull() )
4783 {
4784 return QVariant();
4785 }
4786
4787 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4788
4789 const QVariant v = pointAt( geom, vertexNumber, parent );
4790 if ( !v.isNull() )
4791 return QVariant( v.value<QgsPoint>().y() );
4792 else
4793 return QVariant();
4794}
4795
4796static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4797{
4798 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4799 if ( geom.isNull() )
4800 {
4801 return QVariant();
4802 }
4803
4804 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4805
4806 const QVariant v = pointAt( geom, vertexNumber, parent );
4807 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4808 return QVariant( v.value<QgsPoint>().z() );
4809 else
4810 return QVariant();
4811}
4812
4813static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4814{
4815 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4816 if ( geom.isNull() )
4817 {
4818 return QVariant();
4819 }
4820
4821 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4822
4823 const QVariant v = pointAt( geom, vertexNumber, parent );
4824 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4825 return QVariant( v.value<QgsPoint>().m() );
4826 else
4827 return QVariant();
4828}
4829
4830
4831static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4832{
4833 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4834 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4835 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4836 return result;
4837}
4838
4839static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4840{
4841 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4842 if ( wkb.isNull() )
4843 return QVariant();
4844
4845 QgsGeometry geom;
4846 geom.fromWkb( wkb );
4847 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4848}
4849
4850static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4851{
4852 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4853 QgsOgcUtils::Context ogcContext;
4854 if ( context )
4855 {
4856 QgsWeakMapLayerPointer mapLayerPtr { context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4857 if ( mapLayerPtr )
4858 {
4859 ogcContext.layer = mapLayerPtr.data();
4860 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4861 }
4862 }
4863 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4864 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4865 return result;
4866}
4867
4868static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4869{
4870 FEAT_FROM_CONTEXT( context, f )
4872 QgsDistanceArea *calc = parent->geomCalculator();
4873 if ( calc )
4874 {
4875 try
4876 {
4877 double area = calc->measureArea( f.geometry() );
4878 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4879 return QVariant( area );
4880 }
4881 catch ( QgsCsException & )
4882 {
4883 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4884 return QVariant();
4885 }
4886 }
4887 else
4888 {
4889 return QVariant( f.geometry().area() );
4890 }
4891}
4892
4893static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4894{
4895 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4896
4897 if ( geom.type() != Qgis::GeometryType::Polygon )
4898 return QVariant();
4899
4900 return QVariant( geom.area() );
4901}
4902
4903static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4904{
4905 FEAT_FROM_CONTEXT( context, f )
4907 QgsDistanceArea *calc = parent->geomCalculator();
4908 if ( calc )
4909 {
4910 try
4911 {
4912 double len = calc->measureLength( f.geometry() );
4913 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4914 return QVariant( len );
4915 }
4916 catch ( QgsCsException & )
4917 {
4918 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4919 return QVariant();
4920 }
4921 }
4922 else
4923 {
4924 return QVariant( f.geometry().length() );
4925 }
4926}
4927
4928static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4929{
4930 FEAT_FROM_CONTEXT( context, f )
4932 QgsDistanceArea *calc = parent->geomCalculator();
4933 if ( calc )
4934 {
4935 try
4936 {
4937 double len = calc->measurePerimeter( f.geometry() );
4938 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4939 return QVariant( len );
4940 }
4941 catch ( QgsCsException & )
4942 {
4943 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4944 return QVariant();
4945 }
4946 }
4947 else
4948 {
4949 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4950 }
4951}
4952
4953static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4954{
4955 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4956
4957 if ( geom.type() != Qgis::GeometryType::Polygon )
4958 return QVariant();
4959
4960 //length for polygons = perimeter
4961 return QVariant( geom.length() );
4962}
4963
4964static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4965{
4966 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4967 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4968}
4969
4970static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4971{
4972 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4973 if ( geom.isNull() )
4974 return QVariant();
4975
4976 return QVariant( geom.constGet()->partCount() );
4977}
4978
4979static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4980{
4981 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4982 if ( geom.isNull() )
4983 return QVariant();
4984
4985 return QVariant( geom.isMultipart() );
4986}
4987
4988static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4989{
4990 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4991
4992 if ( geom.isNull() )
4993 return QVariant();
4994
4996 if ( curvePolygon )
4997 return QVariant( curvePolygon->numInteriorRings() );
4998
5000 if ( collection )
5001 {
5002 //find first CurvePolygon in collection
5003 for ( int i = 0; i < collection->numGeometries(); ++i )
5004 {
5005 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5006 if ( !curvePolygon )
5007 continue;
5008
5009 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
5010 }
5011 }
5012
5013 return QVariant();
5014}
5015
5016static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5017{
5018 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5019
5020 if ( geom.isNull() )
5021 return QVariant();
5022
5024 if ( curvePolygon )
5025 return QVariant( curvePolygon->ringCount() );
5026
5027 bool foundPoly = false;
5028 int ringCount = 0;
5030 if ( collection )
5031 {
5032 //find CurvePolygons in collection
5033 for ( int i = 0; i < collection->numGeometries(); ++i )
5034 {
5035 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5036 if ( !curvePolygon )
5037 continue;
5038
5039 foundPoly = true;
5040 ringCount += curvePolygon->ringCount();
5041 }
5042 }
5043
5044 if ( !foundPoly )
5045 return QVariant();
5046
5047 return QVariant( ringCount );
5048}
5049
5050static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5051{
5052 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5053 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
5054 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
5055 return result;
5056}
5057
5058static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5059{
5060 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5061 return QVariant::fromValue( geom.boundingBox().width() );
5062}
5063
5064static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5065{
5066 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5067 return QVariant::fromValue( geom.boundingBox().height() );
5068}
5069
5070static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5071{
5072 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5073 if ( geom.isNull() )
5074 return QVariant();
5075
5077}
5078
5079static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5080{
5081 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5082 return QVariant::fromValue( geom.boundingBox().xMinimum() );
5083}
5084
5085static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5086{
5087 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5088 return QVariant::fromValue( geom.boundingBox().xMaximum() );
5089}
5090
5091static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5092{
5093 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5094 return QVariant::fromValue( geom.boundingBox().yMinimum() );
5095}
5096
5097static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5098{
5099 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5100 return QVariant::fromValue( geom.boundingBox().yMaximum() );
5101}
5102
5103static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5104{
5105 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5106
5107 if ( geom.isNull() || geom.isEmpty() )
5108 return QVariant();
5109
5110 if ( !geom.constGet()->is3D() )
5111 return QVariant();
5112
5113 double max = std::numeric_limits< double >::lowest();
5114
5115 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5116 {
5117 double z = ( *it ).z();
5118
5119 if ( max < z )
5120 max = z;
5121 }
5122
5123 if ( max == std::numeric_limits< double >::lowest() )
5124 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5125
5126 return QVariant( max );
5127}
5128
5129static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5130{
5131 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5132
5133 if ( geom.isNull() || geom.isEmpty() )
5134 return QVariant();
5135
5136 if ( !geom.constGet()->is3D() )
5137 return QVariant();
5138
5139 double min = std::numeric_limits< double >::max();
5140
5141 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5142 {
5143 double z = ( *it ).z();
5144
5145 if ( z < min )
5146 min = z;
5147 }
5148
5149 if ( min == std::numeric_limits< double >::max() )
5150 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5151
5152 return QVariant( min );
5153}
5154
5155static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5156{
5157 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5158
5159 if ( geom.isNull() || geom.isEmpty() )
5160 return QVariant();
5161
5162 if ( !geom.constGet()->isMeasure() )
5163 return QVariant();
5164
5165 double min = std::numeric_limits< double >::max();
5166
5167 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5168 {
5169 double m = ( *it ).m();
5170
5171 if ( m < min )
5172 min = m;
5173 }
5174
5175 if ( min == std::numeric_limits< double >::max() )
5176 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5177
5178 return QVariant( min );
5179}
5180
5181static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5182{
5183 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5184
5185 if ( geom.isNull() || geom.isEmpty() )
5186 return QVariant();
5187
5188 if ( !geom.constGet()->isMeasure() )
5189 return QVariant();
5190
5191 double max = std::numeric_limits< double >::lowest();
5192
5193 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5194 {
5195 double m = ( *it ).m();
5196
5197 if ( max < m )
5198 max = m;
5199 }
5200
5201 if ( max == std::numeric_limits< double >::lowest() )
5202 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5203
5204 return QVariant( max );
5205}
5206
5207static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5208{
5209 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5211 if ( !curve )
5212 {
5213 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5214 return QVariant();
5215 }
5216
5217 return QVariant( curve->sinuosity() );
5218}
5219
5220static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5221{
5222 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5223 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5224 if ( !curve )
5225 {
5226 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5227 return QVariant();
5228 }
5229
5230 return QVariant( curve->straightDistance2d() );
5231}
5232
5233static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5234{
5235 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5237
5238 if ( !poly )
5239 {
5240 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5241 return QVariant();
5242 }
5243
5244 return QVariant( poly->roundness() );
5245}
5246
5247
5248static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5249{
5250 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5251 if ( geom.isNull() )
5252 return QVariant();
5253
5254 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5255 flipped->swapXy();
5256 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5257}
5258
5259static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5260{
5261 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5262 if ( fGeom.isNull() )
5263 return QVariant();
5264
5265 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5266 if ( !curve && fGeom.isMultipart() )
5267 {
5269 {
5270 if ( collection->numGeometries() == 1 )
5271 {
5272 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5273 }
5274 }
5275 }
5276
5277 if ( !curve )
5278 return QVariant();
5279
5280 return QVariant::fromValue( curve->isClosed() );
5281}
5282
5283static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5284{
5285 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5286
5287 if ( geom.isNull() )
5288 return QVariant();
5289
5290 QVariant result;
5291 if ( !geom.isMultipart() )
5292 {
5294
5295 if ( !line )
5296 return QVariant();
5297
5298 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5299 closedLine->close();
5300
5301 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5302 }
5303 else
5304 {
5306 if ( !collection )
5307 return QVariant();
5308
5309 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5310
5311 for ( int i = 0; i < collection->numGeometries(); ++i )
5312 {
5313 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5314 {
5315 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5316 closedLine->close();
5317
5318 closed->addGeometry( closedLine.release() );
5319 }
5320 }
5321 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5322 }
5323
5324 return result;
5325}
5326
5327static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5328{
5329 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5330 if ( fGeom.isNull() )
5331 return QVariant();
5332
5333 return QVariant::fromValue( fGeom.isEmpty() );
5334}
5335
5336static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5337{
5338 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5339 return QVariant::fromValue( true );
5340
5341 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5342 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5343}
5344
5345static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5346{
5347 if ( values.length() < 2 || values.length() > 3 )
5348 return QVariant();
5349
5350 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5351 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5352
5353 if ( fGeom.isNull() || sGeom.isNull() )
5354 return QVariant();
5355
5356 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5357
5358 if ( values.length() == 2 )
5359 {
5360 //two geometry arguments, return relation
5361 QString result = engine->relate( sGeom.constGet() );
5362 return QVariant::fromValue( result );
5363 }
5364 else
5365 {
5366 //three arguments, test pattern
5367 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5368 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5369 return QVariant::fromValue( result );
5370 }
5371}
5372
5373static QVariant fcnBbox( 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.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5378}
5379static QVariant fcnDisjoint( 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.disjoint( sGeom ) ? TVL_True : TVL_False;
5384}
5385static QVariant fcnIntersects( 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.intersects( sGeom ) ? TVL_True : TVL_False;
5390}
5391static QVariant fcnTouches( 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.touches( sGeom ) ? TVL_True : TVL_False;
5396}
5397static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5398{
5399 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5400 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5401 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5402}
5403static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5404{
5405 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5406 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5407 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5408}
5409static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5410{
5411 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5412 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5413 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5414}
5415static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5416{
5417 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5418 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5419 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5420}
5421
5422static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5423{
5424 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5425 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5426 return fGeom.isExactlyEqual( sGeom ) ? TVL_True : TVL_False;
5427}
5428
5429static QVariant fcnIsEqualsExact( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5430{
5431 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5432 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5433 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5434
5435 bool ok;
5436 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5437 if ( !ok )
5438 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5439
5440 QVariant ret = TVL_False;
5441 try
5442 {
5443 ret = fGeom.isExactlyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5444 }
5445 catch ( QgsNotSupportedException &e )
5446 {
5447 SET_EVAL_ERROR( e.what() );
5448 }
5449 return ret;
5450}
5451
5452static QVariant fcnIsEqualsTopological( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5453{
5454 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5455 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5456 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5457
5458 bool ok;
5459 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
5460 if ( !ok )
5461 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5462
5463 QVariant ret = TVL_False;
5464 try
5465 {
5466 ret = fGeom.isTopologicallyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5467 }
5468 catch ( QgsNotSupportedException &e )
5469 {
5470 SET_EVAL_ERROR( e.what() );
5471 }
5472 return ret;
5473}
5474
5475static QVariant fcnIsEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5476{
5477 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5478 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5479 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5480
5481 bool ok;
5482 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5483 if ( !ok )
5484 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5485
5486 double epsilon = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5487
5488 QVariant ret = TVL_False;
5489 try
5490 {
5491 ret = fGeom.isFuzzyEqual( sGeom, epsilon, backend ) ? TVL_True : TVL_False;
5492 }
5493 catch ( QgsNotSupportedException &e )
5494 {
5495 SET_EVAL_ERROR( e.what() );
5496 }
5497 return ret;
5498}
5499
5500static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5501{
5502 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5503 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5504 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5505 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5506 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5507 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5508
5510 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5511 capStyle = Qgis::EndCapStyle::Flat;
5512 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5513 capStyle = Qgis::EndCapStyle::Square;
5514
5516 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5517 joinStyle = Qgis::JoinStyle::Miter;
5518 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5519 joinStyle = Qgis::JoinStyle::Bevel;
5520
5521 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5522 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5523 return result;
5524}
5525
5526static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5527{
5528 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5529 const QgsGeometry reoriented = fGeom.forceRHR();
5530 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5531}
5532
5533static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5534{
5535 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5536 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5537 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5538}
5539
5540static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5541{
5542 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5543 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5544 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5545}
5546
5547static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5548{
5549 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5551 if ( !pt && fGeom.isMultipart() )
5552 {
5554 {
5555 if ( collection->numGeometries() == 1 )
5556 {
5557 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5558 }
5559 }
5560 }
5561
5562 if ( !pt )
5563 {
5564 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5565 return QVariant();
5566 }
5567
5568 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5569 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5570 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5571 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5572
5573 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5574 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5575 return result;
5576}
5577
5578static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5579{
5580 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5581 if ( fGeom.type() != Qgis::GeometryType::Line )
5582 {
5583 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5584 return QVariant();
5585 }
5586
5587 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5588 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5589 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5590
5591 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5592 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5593 return result;
5594}
5595
5596static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5597{
5598 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5599 if ( fGeom.type() != Qgis::GeometryType::Line )
5600 {
5601 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5602 return QVariant();
5603 }
5604
5605 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5606
5607 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5608 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5609 return result;
5610}
5611
5612static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5613{
5614 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5615 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5616 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5617 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5618 if ( joinInt < 1 || joinInt > 3 )
5619 return QVariant();
5620 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5621
5622 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5623
5624 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5625 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5626 return result;
5627}
5628
5629static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5630{
5631 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5632 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5633 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5634
5635 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5636 if ( joinInt < 1 || joinInt > 3 )
5637 return QVariant();
5638 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5639
5640 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5641
5642 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5643 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5644 return result;
5645}
5646
5647static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5648{
5649 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5650 const double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5651 const double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5652
5653 const double deflectionStart = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5654 const double deflectionEnd = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5655
5656 QgsGeometry geom = fGeom.extendLine( distStart, distEnd, deflectionStart, deflectionEnd );
5657 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5658 return result;
5659}
5660
5661static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5662{
5663 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5664 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5665 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5666 fGeom.translate( dx, dy );
5667 return QVariant::fromValue( fGeom );
5668}
5669
5670static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5671{
5672 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5673 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5674 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent ) : QgsGeometry();
5675 const bool perPart = values.value( 3 ).toBool();
5676
5677 if ( center.isNull() && perPart && fGeom.isMultipart() )
5678 {
5679 // no explicit center, rotating per part
5680 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5681 // the result is equivalent to setting perPart as false anyway)
5682 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5683 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5684 {
5685 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5686 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5687 t.rotate( -rotation );
5688 t.translate( -partCenter.x(), -partCenter.y() );
5689 ( *it )->transform( t );
5690 }
5691 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5692 }
5693 else
5694 {
5695 QgsPointXY pt;
5696 if ( center.isEmpty() )
5697 {
5698 // if center wasn't specified, use bounding box centroid
5699 pt = fGeom.boundingBox().center();
5700 }
5702 {
5703 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5704 return QVariant();
5705 }
5706 else
5707 {
5709 }
5710
5711 fGeom.rotate( rotation, pt );
5712 return QVariant::fromValue( fGeom );
5713 }
5714}
5715
5716static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5717{
5718 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5719 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5720 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5721 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent ) : QgsGeometry();
5722
5723 QgsPointXY pt;
5724 if ( center.isNull() )
5725 {
5726 // if center wasn't specified, use bounding box centroid
5727 pt = fGeom.boundingBox().center();
5728 }
5730 {
5731 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5732 return QVariant();
5733 }
5734 else
5735 {
5736 pt = center.asPoint();
5737 }
5738
5739 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5740 t.scale( xScale, yScale );
5741 t.translate( -pt.x(), -pt.y() );
5742 fGeom.transform( t );
5743 return QVariant::fromValue( fGeom );
5744}
5745
5746static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5747{
5748 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5749 if ( fGeom.isNull() )
5750 {
5751 return QVariant();
5752 }
5753
5754 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5755 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5756
5757 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5758
5759 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5760 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5761
5762 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5763 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5764 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5765 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5766
5767 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5768 {
5769 fGeom.get()->addZValue( 0 );
5770 }
5771 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5772 {
5773 fGeom.get()->addMValue( 0 );
5774 }
5775
5776 QTransform transform;
5777 transform.translate( deltaX, deltaY );
5778 transform.rotate( rotationZ );
5779 transform.scale( scaleX, scaleY );
5780 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5781
5782 return QVariant::fromValue( fGeom );
5783}
5784
5785
5786static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5787{
5788 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5789 QgsGeometry geom = fGeom.centroid();
5790 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5791 return result;
5792}
5793static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5794{
5795 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5796 QgsGeometry geom = fGeom.pointOnSurface();
5797 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5798 return result;
5799}
5800
5801static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5802{
5803 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5804 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5805 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5806 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5807 return result;
5808}
5809
5810static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5811{
5812 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5813 QgsGeometry geom = fGeom.convexHull();
5814 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5815 return result;
5816}
5817
5818#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
5819static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5820{
5821 try
5822 {
5823 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5824 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5825 const bool allowHoles = values.value( 2 ).toBool();
5826 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5827 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5828 return result;
5829 }
5830 catch ( QgsCsException &cse )
5831 {
5832 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5833 return QVariant();
5834 }
5835}
5836#endif
5837
5838static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5839{
5840 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5841 int segments = 36;
5842 if ( values.length() == 2 )
5843 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5844 if ( segments < 0 )
5845 {
5846 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5847 return QVariant();
5848 }
5849
5850 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5851 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5852 return result;
5853}
5854
5855static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5856{
5857 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5859 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5860 return result;
5861}
5862
5863static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5864{
5865 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5866
5867 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5868 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5869 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5870
5871 double area, angle, width, height;
5872 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5873
5874 if ( geom.isNull() )
5875 {
5876 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5877 return QVariant();
5878 }
5879 return angle;
5880}
5881
5882static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5883{
5884 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5885 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5886 QgsGeometry geom = fGeom.difference( sGeom );
5887 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5888 return result;
5889}
5890
5891static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5892{
5893 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5894 return QVariant();
5895
5896 // two variants, one for geometry, one for string
5897
5898 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5899 if ( !fGeom.isNull() )
5900 {
5901 QVariant result;
5902 if ( !fGeom.isMultipart() )
5903 {
5904 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5905 if ( !curve )
5906 return QVariant();
5907
5908 QgsCurve *reversed = curve->reversed();
5909 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5910 }
5911 else
5912 {
5914 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5915 for ( int i = 0; i < collection->numGeometries(); ++i )
5916 {
5917 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5918 {
5919 reversed->addGeometry( curve->reversed() );
5920 }
5921 else
5922 {
5923 reversed->addGeometry( collection->geometryN( i )->clone() );
5924 }
5925 }
5926 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5927 }
5928 return result;
5929 }
5930
5931 //fall back to string variant
5932 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5933 std::reverse( string.begin(), string.end() );
5934 return string;
5935}
5936
5937static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5938{
5939 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5940 if ( fGeom.isNull() )
5941 return QVariant();
5942
5944 if ( !curvePolygon && fGeom.isMultipart() )
5945 {
5947 {
5948 if ( collection->numGeometries() == 1 )
5949 {
5950 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5951 }
5952 }
5953 }
5954
5955 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5956 return QVariant();
5957
5958 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5959 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5960 return result;
5961}
5962
5963static QVariant fcnDistance( 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 return QVariant( fGeom.distance( sGeom ) );
5968}
5969
5970static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5971{
5972 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5973 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5974
5975 double res = -1;
5976 if ( values.length() == 3 && values.at( 2 ).isValid() )
5977 {
5978 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5979 densify = std::clamp( densify, 0.0, 1.0 );
5980 res = g1.hausdorffDistanceDensify( g2, densify );
5981 }
5982 else
5983 {
5984 res = g1.hausdorffDistance( g2 );
5985 }
5986
5987 return res > -1 ? QVariant( res ) : QVariant();
5988}
5989
5990static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5991{
5992 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5993 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5994 QgsGeometry geom = fGeom.intersection( sGeom );
5995 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5996 return result;
5997}
5998static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5999{
6000 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6001 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6002 QgsGeometry geom = fGeom.symDifference( sGeom );
6003 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6004 return result;
6005}
6006static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6007{
6008 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6009 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6010 QgsGeometry geom = fGeom.combine( sGeom );
6011 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6012 return result;
6013}
6014
6015static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6016{
6017 if ( values.length() < 1 || values.length() > 2 )
6018 return QVariant();
6019
6020 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6021 int prec = 8;
6022 if ( values.length() == 2 )
6023 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6024 QString wkt = fGeom.asWkt( prec );
6025 return QVariant( wkt );
6026}
6027
6028static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6029{
6030 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6031 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
6032}
6033
6034static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6035{
6036 if ( values.length() != 2 )
6037 {
6038 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
6039 return QVariant();
6040 }
6041
6042 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6043 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6044
6045 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6046 if ( !pt1 && fGeom1.isMultipart() )
6047 {
6049 {
6050 if ( collection->numGeometries() == 1 )
6051 {
6052 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6053 }
6054 }
6055 }
6056
6057 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6058 if ( !pt2 && fGeom2.isMultipart() )
6059 {
6061 {
6062 if ( collection->numGeometries() == 1 )
6063 {
6064 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6065 }
6066 }
6067 }
6068
6069 if ( !pt1 || !pt2 )
6070 {
6071 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
6072 return QVariant();
6073 }
6074
6075 // Code from PostGIS
6076 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
6077 {
6078 if ( pt1->y() < pt2->y() )
6079 return 0.0;
6080 else if ( pt1->y() > pt2->y() )
6081 return M_PI;
6082 else
6083 return 0;
6084 }
6085
6086 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
6087 {
6088 if ( pt1->x() < pt2->x() )
6089 return M_PI_2;
6090 else if ( pt1->x() > pt2->x() )
6091 return M_PI + ( M_PI_2 );
6092 else
6093 return 0;
6094 }
6095
6096 if ( pt1->x() < pt2->x() )
6097 {
6098 if ( pt1->y() < pt2->y() )
6099 {
6100 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
6101 }
6102 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
6103 {
6104 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI_2 );
6105 }
6106 }
6107
6108 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
6109 {
6110 if ( pt1->y() > pt2->y() )
6111 {
6112 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI;
6113 }
6114 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
6115 {
6116 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI_2 ) );
6117 }
6118 }
6119}
6120
6121static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6122{
6123 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6124 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6125 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6126 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
6127
6128 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
6129 {
6130 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
6131 return QVariant();
6132 }
6133
6134 const QgsPointXY point1 = geom1.asPoint();
6135 const QgsPointXY point2 = geom2.asPoint();
6136 if ( point1.isEmpty() || point2.isEmpty() )
6137 {
6138 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
6139 return QVariant();
6140 }
6141
6143 if ( context )
6144 {
6145 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6146
6147 if ( !sourceCrs.isValid() )
6148 {
6149 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
6150 }
6151
6152 if ( ellipsoid.isEmpty() )
6153 {
6154 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
6155 }
6156 }
6157
6158 if ( !sourceCrs.isValid() )
6159 {
6160 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
6161 return QVariant();
6162 }
6163
6164 QgsDistanceArea da;
6165 da.setSourceCrs( sourceCrs, tContext );
6166 if ( !da.setEllipsoid( ellipsoid ) )
6167 {
6168 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
6169 return QVariant();
6170 }
6171
6172 try
6173 {
6174 const double bearing = da.bearing( point1, point2 );
6175 if ( std::isfinite( bearing ) )
6176 {
6177 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
6178 }
6179 }
6180 catch ( QgsCsException &cse )
6181 {
6182 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
6183 return QVariant();
6184 }
6185 return QVariant();
6186}
6187
6188static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6189{
6190 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6191
6193 {
6194 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
6195 return QVariant();
6196 }
6197
6198 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6199 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6200 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
6201
6202 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef() );
6203 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
6204
6205 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
6206}
6207
6208static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6209{
6210 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6211 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6212
6213 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6214 if ( !pt1 && fGeom1.isMultipart() )
6215 {
6217 {
6218 if ( collection->numGeometries() == 1 )
6219 {
6220 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6221 }
6222 }
6223 }
6224 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6225 if ( !pt2 && fGeom2.isMultipart() )
6226 {
6228 {
6229 if ( collection->numGeometries() == 1 )
6230 {
6231 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6232 }
6233 }
6234 }
6235
6236 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) || !pt1 || !pt2 )
6237 {
6238 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6239 return QVariant();
6240 }
6241
6242 return pt1->inclination( *pt2 );
6243}
6244
6245static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6246{
6247 if ( values.length() != 3 )
6248 return QVariant();
6249
6250 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6251 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6252 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6253
6254 QgsGeometry geom = fGeom.extrude( x, y );
6255
6256 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6257 return result;
6258}
6259
6260static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6261{
6262 if ( values.length() < 2 )
6263 return QVariant();
6264
6265 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6266
6267 if ( !fGeom.isMultipart() )
6268 return values.at( 0 );
6269
6270 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6271 QVariant cachedExpression;
6272 if ( ctx )
6273 cachedExpression = ctx->cachedValue( expString );
6274 QgsExpression expression;
6275
6276 if ( cachedExpression.isValid() )
6277 {
6278 expression = cachedExpression.value<QgsExpression>();
6279 }
6280 else
6281 expression = QgsExpression( expString );
6282
6283 bool asc = values.value( 2 ).toBool();
6284
6285 QgsExpressionContext *unconstedContext = nullptr;
6286 QgsFeature f;
6287 if ( ctx )
6288 {
6289 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6290 // so no reason to worry
6291 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6292 f = ctx->feature();
6293 }
6294 else
6295 {
6296 // If there's no context provided, create a fake one
6297 unconstedContext = new QgsExpressionContext();
6298 }
6299
6301 Q_ASSERT( collection ); // Should have failed the multipart check above
6302
6304 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6305 QgsExpressionSorter sorter( orderBy );
6306
6307 QList<QgsFeature> partFeatures;
6308 partFeatures.reserve( collection->partCount() );
6309 for ( int i = 0; i < collection->partCount(); ++i )
6310 {
6311 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6312 partFeatures << f;
6313 }
6314
6315 sorter.sortFeatures( partFeatures, unconstedContext );
6316
6318
6319 Q_ASSERT( orderedGeom );
6320
6321 while ( orderedGeom->partCount() )
6322 orderedGeom->removeGeometry( 0 );
6323
6324 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6325 {
6326 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6327 }
6328
6329 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6330
6331 if ( !ctx )
6332 delete unconstedContext;
6333
6334 return result;
6335}
6336
6337static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6338{
6339 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6340 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6341
6342 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6343
6344 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6345 return result;
6346}
6347
6348static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6349{
6350 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6351 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6352
6353 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6354
6355 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6356 return result;
6357}
6358
6359static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6360{
6361 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6362 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6363
6364 QgsGeometry geom = lineGeom.interpolate( distance );
6365
6366 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6367 return result;
6368}
6369
6370static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6371{
6372 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6373 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6374 const bool use3DDistance = values.at( 2 ).toBool();
6375
6376 double x, y, z, distance;
6377
6379 if ( !line )
6380 {
6381 return QVariant();
6382 }
6383
6384 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6385 {
6386 QgsPoint point( x, y );
6387 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6388 {
6389 point.addZValue( z );
6390 }
6391 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6392 }
6393
6394 return QVariant();
6395}
6396
6397static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6398{
6399 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6400 if ( lineGeom.type() != Qgis::GeometryType::Line )
6401 {
6402 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6403 return QVariant();
6404 }
6405
6406 const QgsCurve *curve = nullptr;
6407 if ( !lineGeom.isMultipart() )
6408 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6409 else
6410 {
6412 {
6413 if ( collection->numGeometries() > 0 )
6414 {
6415 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6416 }
6417 }
6418 }
6419 if ( !curve )
6420 return QVariant();
6421
6422 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6423 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6424
6425 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6426 QgsGeometry result( std::move( substring ) );
6427 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6428}
6429
6430static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6431{
6432 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6433 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6434
6435 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6436}
6437
6438static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6439{
6440 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6441 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6442 if ( vertex < 0 )
6443 {
6444 //negative idx
6445 int count = geom.constGet()->nCoordinates();
6446 vertex = count + vertex;
6447 }
6448
6449 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6450}
6451
6452static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6453{
6454 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6455 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6456 if ( vertex < 0 )
6457 {
6458 //negative idx
6459 int count = geom.constGet()->nCoordinates();
6460 vertex = count + vertex;
6461 }
6462
6463 return geom.distanceToVertex( vertex );
6464}
6465
6466static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6467{
6468 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6469 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6470
6471 double distance = lineGeom.lineLocatePoint( pointGeom );
6472
6473 return distance >= 0 ? distance : QVariant();
6474}
6475
6476static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6477{
6478 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6479 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6480 const bool use3DDistance = values.at( 2 ).toBool();
6481
6482 double x, y, z, distance;
6483
6485 if ( !line )
6486 {
6487 return QVariant();
6488 }
6489
6490 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6491 return found ? distance : QVariant();
6492}
6493
6494static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6495{
6496 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6497 {
6498 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6499 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6500 }
6501
6502 if ( values.length() >= 1 )
6503 {
6504 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6505 return QVariant( qlonglong( std::round( number ) ) );
6506 }
6507
6508 return QVariant();
6509}
6510
6511static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6512{
6513 Q_UNUSED( values )
6514 Q_UNUSED( parent )
6515 return M_PI;
6516}
6517
6518static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6519{
6520 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6521 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6522 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6523 if ( places < 0 )
6524 {
6525 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6526 return QVariant();
6527 }
6528
6529 const bool omitGroupSeparator = values.value( 3 ).toBool();
6530 const bool trimTrailingZeros = values.value( 4 ).toBool();
6531
6532 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6533 if ( !omitGroupSeparator )
6534 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6535 else
6536 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6537
6538 QString res = locale.toString( value, 'f', places );
6539
6540 if ( trimTrailingZeros )
6541 {
6542 const QChar decimal = locale.decimalPoint().at( 0 );
6543 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6544
6545 if ( res.contains( decimal ) )
6546 {
6547 int trimPoint = res.length() - 1;
6548
6549 while ( res.at( trimPoint ) == zeroDigit )
6550 trimPoint--;
6551
6552 if ( res.at( trimPoint ) == decimal )
6553 trimPoint--;
6554
6555 res.truncate( trimPoint + 1 );
6556 }
6557 }
6558
6559 return res;
6560}
6561
6562static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6563{
6564 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6565 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6566 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6567
6568 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6569 if ( format.indexOf( "Z" ) > 0 )
6570 datetime = datetime.toUTC();
6571
6572 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6573 return locale.toString( datetime, format );
6574}
6575
6576static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6577{
6578 const QVariant variant = values.at( 0 );
6579 bool isQColor;
6580 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6581 if ( !color.isValid() )
6582 return QVariant();
6583
6584 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6585 if ( color.spec() == QColor::Spec::Cmyk )
6586 {
6587 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() )
6588 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6589 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6590 }
6591 else
6592 {
6593 const float avg = ( color.redF() + color.greenF() + color.blueF() )
6594 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6595 color.setRgbF( avg, avg, avg, alpha );
6596 }
6597
6598 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6599}
6600
6601static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6602{
6603 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6604 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6605 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6606 if ( ratio > 1 )
6607 {
6608 ratio = 1;
6609 }
6610 else if ( ratio < 0 )
6611 {
6612 ratio = 0;
6613 }
6614
6615 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6616 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6617 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6618 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6619
6620 QColor newColor( red, green, blue, alpha );
6621
6622 return QgsSymbolLayerUtils::encodeColor( newColor );
6623}
6624
6625static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6626{
6627 const QVariant variant1 = values.at( 0 );
6628 const QVariant variant2 = values.at( 1 );
6629
6630 if ( variant1.userType() != variant2.userType() )
6631 {
6632 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6633 return QVariant();
6634 }
6635
6636 bool isQColor;
6637 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6638 if ( !color1.isValid() )
6639 return QVariant();
6640
6641 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6642 if ( !color2.isValid() )
6643 return QVariant();
6644
6645 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6646 {
6647 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6648 return QVariant();
6649 }
6650
6651 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6652
6653 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6654 // NOLINTBEGIN(bugprone-narrowing-conversions)
6655
6656 QColor newColor;
6657 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6658 if ( color1.spec() == QColor::Spec::Cmyk )
6659 {
6660 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6661 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6662 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6663 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6664 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6665 }
6666 else
6667 {
6668 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6669 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6670 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6671 newColor = QColor::fromRgbF( red, green, blue, alpha );
6672 }
6673
6674 // NOLINTEND(bugprone-narrowing-conversions)
6675
6676 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6677}
6678
6679static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6680{
6681 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6682 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6683 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6684 QColor color = QColor( red, green, blue );
6685 if ( !color.isValid() )
6686 {
6687 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6688 color = QColor( 0, 0, 0 );
6689 }
6690
6691 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6692}
6693
6694static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6695{
6696 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6697 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6698 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6699 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6700 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6701 if ( !color.isValid() )
6702 {
6703 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6704 return QVariant();
6705 }
6706
6707 return color;
6708}
6709
6710static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6711{
6712 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6713 QVariant value = node->eval( parent, context );
6714 if ( parent->hasEvalError() )
6715 {
6716 parent->setEvalErrorString( QString() );
6717 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6719 value = node->eval( parent, context );
6721 }
6722 return value;
6723}
6724
6725static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6726{
6727 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6729 QVariant value = node->eval( parent, context );
6731 if ( value.toBool() )
6732 {
6733 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6735 value = node->eval( parent, context );
6737 }
6738 else
6739 {
6740 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6742 value = node->eval( parent, context );
6744 }
6745 return value;
6746}
6747
6748static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6749{
6750 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6751 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6752 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6753 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6754 QColor color = QColor( red, green, blue, alpha );
6755 if ( !color.isValid() )
6756 {
6757 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6758 color = QColor( 0, 0, 0 );
6759 }
6760 return QgsSymbolLayerUtils::encodeColor( color );
6761}
6762
6763QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6764{
6765 QgsGradientColorRamp expRamp;
6766 const QgsColorRamp *ramp = nullptr;
6767 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6768 {
6769 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6770 ramp = &expRamp;
6771 }
6772 else
6773 {
6774 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6775 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6776 if ( !ramp )
6778 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6779 return QVariant();
6780 }
6781 }
6782
6783 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6784 QColor color = ramp->color( value );
6785 return color;
6786}
6787
6788QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6789{
6790 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6791 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6792}
6793
6794static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6795{
6796 // Hue ranges from 0 - 360
6797 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6798 // Saturation ranges from 0 - 100
6799 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6800 // Lightness ranges from 0 - 100
6801 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6802
6803 QColor color = QColor::fromHslF( hue, saturation, lightness );
6804
6805 if ( !color.isValid() )
6806 {
6807 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6808 color = QColor( 0, 0, 0 );
6809 }
6810
6811 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6812}
6813
6814static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6815{
6816 // Hue ranges from 0 - 360
6817 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6818 // Saturation ranges from 0 - 100
6819 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6820 // Lightness ranges from 0 - 100
6821 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6822 // Alpha ranges from 0 - 255
6823 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6824
6825 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6826 if ( !color.isValid() )
6827 {
6828 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6829 color = QColor( 0, 0, 0 );
6830 }
6831 return QgsSymbolLayerUtils::encodeColor( color );
6832}
6833
6834static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6835{
6836 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6837 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6838 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6839 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6840
6841 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6842 if ( !color.isValid() )
6843 {
6844 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6845 return QVariant();
6846 }
6847
6848 return color;
6849}
6850
6851static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6852{
6853 // Hue ranges from 0 - 360
6854 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6855 // Saturation ranges from 0 - 100
6856 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6857 // Value ranges from 0 - 100
6858 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6859
6860 QColor color = QColor::fromHsvF( hue, saturation, value );
6861
6862 if ( !color.isValid() )
6863 {
6864 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6865 color = QColor( 0, 0, 0 );
6866 }
6867
6868 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6869}
6870
6871static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6872{
6873 // Hue ranges from 0 - 360
6874 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6875 // Saturation ranges from 0 - 100
6876 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6877 // Value ranges from 0 - 100
6878 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6879 // Alpha ranges from 0 - 255
6880 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6881
6882 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6883 if ( !color.isValid() )
6884 {
6885 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6886 color = QColor( 0, 0, 0 );
6887 }
6888 return QgsSymbolLayerUtils::encodeColor( color );
6889}
6890
6891static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6892{
6893 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6894 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6895 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6896 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6897 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6898
6899 if ( !color.isValid() )
6900 {
6901 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6902 return QVariant();
6903 }
6904
6905 return color;
6906}
6907
6908static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6909{
6910 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6911 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6912 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6913 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6914 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6915
6916 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6917 if ( !color.isValid() )
6918 {
6919 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6920 return QVariant();
6921 }
6922
6923 return color;
6924}
6925
6926static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6927{
6928 // Cyan ranges from 0 - 100
6929 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6930 // Magenta ranges from 0 - 100
6931 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6932 // Yellow ranges from 0 - 100
6933 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6934 // Black ranges from 0 - 100
6935 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6936
6937 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6938
6939 if ( !color.isValid() )
6940 {
6941 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6942 color = QColor( 0, 0, 0 );
6943 }
6944
6945 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6946}
6947
6948static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6949{
6950 // Cyan ranges from 0 - 100
6951 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6952 // Magenta ranges from 0 - 100
6953 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6954 // Yellow ranges from 0 - 100
6955 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6956 // Black ranges from 0 - 100
6957 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6958 // Alpha ranges from 0 - 255
6959 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6960
6961 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6962 if ( !color.isValid() )
6963 {
6964 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6965 color = QColor( 0, 0, 0 );
6966 }
6967 return QgsSymbolLayerUtils::encodeColor( color );
6968}
6969
6970static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6971{
6972 const QVariant variant = values.at( 0 );
6973 bool isQColor;
6974 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6975 if ( !color.isValid() )
6976 return QVariant();
6977
6978 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6979 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6980 return color.red();
6981 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6982 return color.green();
6983 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6984 return color.blue();
6985 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6986 return color.alpha();
6987 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6988 return static_cast< double >( color.hsvHueF() * 360 );
6989 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6990 return static_cast< double >( color.hsvSaturationF() * 100 );
6991 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6992 return static_cast< double >( color.valueF() * 100 );
6993 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6994 return static_cast< double >( color.hslHueF() * 360 );
6995 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6996 return static_cast< double >( color.hslSaturationF() * 100 );
6997 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6998 return static_cast< double >( color.lightnessF() * 100 );
6999 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7000 return static_cast< double >( color.cyanF() * 100 );
7001 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7002 return static_cast< double >( color.magentaF() * 100 );
7003 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7004 return static_cast< double >( color.yellowF() * 100 );
7005 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7006 return static_cast< double >( color.blackF() * 100 );
7007
7008 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7009 return QVariant();
7010}
7011
7012static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7013{
7014 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7015 if ( map.empty() )
7016 {
7017 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
7018 return QVariant();
7019 }
7020
7021 QList< QColor > colors;
7023 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7024 {
7025 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
7026 if ( !colors.last().isValid() )
7027 {
7028 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
7029 return QVariant();
7030 }
7031
7032 double step = it.key().toDouble();
7033 if ( it == map.constBegin() )
7034 {
7035 if ( step != 0.0 )
7036 stops << QgsGradientStop( step, colors.last() );
7037 }
7038 else if ( it == map.constEnd() )
7039 {
7040 if ( step != 1.0 )
7041 stops << QgsGradientStop( step, colors.last() );
7042 }
7043 else
7044 {
7045 stops << QgsGradientStop( step, colors.last() );
7046 }
7047 }
7048 bool discrete = values.at( 1 ).toBool();
7049
7050 if ( colors.empty() )
7051 return QVariant();
7052
7053 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
7054}
7055
7056static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7057{
7058 const QVariant variant = values.at( 0 );
7059 bool isQColor;
7060 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7061 if ( !color.isValid() )
7062 return QVariant();
7063
7064 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7065 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7066 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
7067 color.setRed( std::clamp( value, 0, 255 ) );
7068 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
7069 color.setGreen( std::clamp( value, 0, 255 ) );
7070 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
7071 color.setBlue( std::clamp( value, 0, 255 ) );
7072 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
7073 color.setAlpha( std::clamp( value, 0, 255 ) );
7074 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7075 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
7076 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7077 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
7078 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7079 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7080 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7081 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
7082 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7083 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
7084 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7085 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7086 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7087 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
7088 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7089 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
7090 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7091 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
7092 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7093 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7094 else
7095 {
7096 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7097 return QVariant();
7098 }
7099 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7100}
7101
7102static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7103{
7104 const QVariant variant = values.at( 0 );
7105 bool isQColor;
7106 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7107 if ( !color.isValid() )
7108 return QVariant();
7109
7110 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7111
7112 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7113}
7114
7115static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7116{
7117 const QVariant variant = values.at( 0 );
7118 bool isQColor;
7119 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7120 if ( !color.isValid() )
7121 return QVariant();
7122
7123 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7124
7125 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7126}
7127
7128static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7129{
7130 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7131 QgsGeometry geom = feat.geometry();
7132 if ( !geom.isNull() )
7133 return QVariant::fromValue( geom );
7134 return QVariant();
7135}
7136
7137static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7138{
7139 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7140 if ( !feat.isValid() )
7141 return QVariant();
7142 return feat.id();
7143}
7144
7145static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7146{
7147 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
7148 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
7149 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
7150
7151 if ( !sCrs.isValid() )
7152 return QVariant::fromValue( fGeom );
7153
7154 if ( !dCrs.isValid() )
7155 return QVariant::fromValue( fGeom );
7156
7158 if ( context )
7159 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
7160 QgsCoordinateTransform t( sCrs, dCrs, tContext );
7161 try
7162 {
7164 return QVariant::fromValue( fGeom );
7165 }
7166 catch ( QgsCsException &cse )
7167 {
7168 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
7169 return QVariant();
7170 }
7171 return QVariant();
7172}
7173
7174
7175static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7176{
7177 bool foundLayer = false;
7178 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7179
7180 //no layer found
7181 if ( !featureSource || !foundLayer )
7182 {
7183 return QVariant();
7184 }
7185
7186 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
7187
7189 req.setFilterFid( fid );
7190 req.setTimeout( 10000 );
7191 req.setRequestMayBeNested( true );
7192 if ( context )
7193 req.setFeedback( context->feedback() );
7194 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7195
7196 QgsFeature fet;
7197 QVariant result;
7198 if ( fIt.nextFeature( fet ) )
7199 result = QVariant::fromValue( fet );
7200
7201 return result;
7202}
7203
7204static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7205{
7206 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
7207 bool foundLayer = false;
7208 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7209
7210 //no layer found
7211 if ( !featureSource || !foundLayer )
7212 {
7213 return QVariant();
7214 }
7216 QString cacheValueKey;
7217 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7218 {
7219 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7220
7221 QMap<QString, QVariant>::const_iterator i = attributeMap.constBegin();
7222 QString filterString;
7223 for ( ; i != attributeMap.constEnd(); ++i )
7224 {
7225 if ( !filterString.isEmpty() )
7226 {
7227 filterString.append( " AND " );
7228 }
7229 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7230 }
7231 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7232 if ( context && context->hasCachedValue( cacheValueKey ) )
7233 {
7234 return context->cachedValue( cacheValueKey );
7235 }
7236 req.setFilterExpression( filterString );
7237 }
7238 else
7239 {
7240 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7241 int attributeId = featureSource->fields().lookupField( attribute );
7242 if ( attributeId == -1 )
7243 {
7244 return QVariant();
7245 }
7246
7247 const QVariant &attVal = values.at( 2 );
7248
7249 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7250 if ( context && context->hasCachedValue( cacheValueKey ) )
7251 {
7252 return context->cachedValue( cacheValueKey );
7253 }
7254
7256 }
7257 req.setLimit( 1 );
7258 req.setTimeout( 10000 );
7259 req.setRequestMayBeNested( true );
7260 if ( context )
7261 req.setFeedback( context->feedback() );
7262 if ( !parent->needsGeometry() )
7263 {
7265 }
7266 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7267
7268 QgsFeature fet;
7269 QVariant res;
7270 if ( fIt.nextFeature( fet ) )
7271 {
7272 res = QVariant::fromValue( fet );
7273 }
7274
7275 if ( context )
7276 context->setCachedValue( cacheValueKey, res );
7277 return res;
7278}
7279
7280static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7281{
7282 QVariant result;
7283 QString fieldName;
7284
7285 if ( context )
7286 {
7287 if ( !values.isEmpty() )
7288 {
7289 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7290 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7291 fieldName = col->name();
7292 else if ( values.size() == 2 )
7293 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7294 }
7295
7296 QVariant value = values.at( 0 );
7297
7298 const QgsFields fields = context->fields();
7299 int fieldIndex = fields.lookupField( fieldName );
7300
7301 if ( fieldIndex == -1 )
7302 {
7303 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7304 }
7305 else
7306 {
7307 // TODO this function is NOT thread safe
7309 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7311
7312 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7313 if ( context->hasCachedValue( cacheValueKey ) )
7314 {
7315 return context->cachedValue( cacheValueKey );
7316 }
7317
7318 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7320
7321 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7322
7323 QVariant cache;
7324 if ( !context->hasCachedValue( cacheKey ) )
7325 {
7326 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7327 context->setCachedValue( cacheKey, cache );
7328 }
7329 else
7330 cache = context->cachedValue( cacheKey );
7331
7332 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7333
7334 context->setCachedValue( cacheValueKey, result );
7335 }
7336 }
7337 else
7338 {
7339 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7340 }
7341
7342 return result;
7343}
7344
7345static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7346{
7347 const QVariant data = values.at( 0 );
7348 const QMimeDatabase db;
7349 return db.mimeTypeForData( data.toByteArray() ).name();
7350}
7351
7352static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7353{
7354 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7355
7356 bool translate = true;
7357 if ( values.length() >= 3 )
7358 {
7359 translate = QgsExpressionUtils::getTVLValue( values.at( 2 ), parent ) == QgsExpressionUtils::TVL::True;
7360 }
7361
7362 bool foundLayer = false;
7363 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7364 values.at( 0 ),
7365 context,
7366 parent,
7367 [layerProperty, translate]( QgsMapLayer *layer ) -> QVariant {
7368 if ( !layer )
7369 return QVariant();
7370
7371 // here, we always prefer the layer metadata values over the older server-specific published values
7372 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7373 return layer->name();
7374 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7375 return layer->id();
7376 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7377 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7378 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7379 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7380 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7381 {
7382 QStringList keywords;
7383 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7384 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7385 {
7386 keywords.append( it.value() );
7387 }
7388 if ( !keywords.isEmpty() )
7389 return keywords;
7390 return layer->serverProperties()->keywordList();
7391 }
7392 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7393 return layer->serverProperties()->dataUrl();
7394 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7395 {
7396 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7397 }
7398 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7399 return layer->serverProperties()->attributionUrl();
7400 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7401 return layer->publicSource();
7402 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7403 return layer->minimumScale();
7404 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7405 return layer->maximumScale();
7406 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7407 return layer->isEditable();
7408 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7409 return layer->crs().authid();
7410 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7411 return layer->crs().toProj();
7412 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7413 return layer->crs().description();
7414 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7415 return layer->crs().ellipsoidAcronym();
7416 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7417 {
7418 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7419 QVariant result = QVariant::fromValue( extentGeom );
7420 return result;
7421 }
7422 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7423 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7424 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7425 {
7426 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7427 return decodedUri.value( u"path"_s );
7428 }
7429 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7430 {
7431 switch ( layer->type() )
7432 {
7434 return translate ? QCoreApplication::translate( "expressions", "Vector" ) : u"Vector"_s;
7436 return translate ? QCoreApplication::translate( "expressions", "Raster" ) : u"Raster"_s;
7438 return translate ? QCoreApplication::translate( "expressions", "Mesh" ) : u"Mesh"_s;
7440 return translate ? QCoreApplication::translate( "expressions", "Vector Tile" ) : u"Vector Tile"_s;
7442 return translate ? QCoreApplication::translate( "expressions", "Plugin" ) : u"Plugin"_s;
7444 return translate ? QCoreApplication::translate( "expressions", "Annotation" ) : u"Annotation"_s;
7446 return translate ? QCoreApplication::translate( "expressions", "Point Cloud" ) : u"Point Cloud"_s;
7448 return translate ? QCoreApplication::translate( "expressions", "Group" ) : u"Group"_s;
7450 return translate ? QCoreApplication::translate( "expressions", "Tiled Scene" ) : u"Tiled Scene"_s;
7451 }
7452 }
7453 else
7454 {
7455 //vector layer methods
7456 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7457 if ( vLayer )
7458 {
7459 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7460 return vLayer->storageType();
7461 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7463 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7464 return QVariant::fromValue( vLayer->featureCount() );
7465 }
7466 }
7467
7468 return QVariant();
7469 },
7470 foundLayer
7471 );
7472
7473 if ( !foundLayer )
7474 return QVariant();
7475 else
7476 return res;
7477}
7478
7479static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7480{
7481 const QString uriPart = values.at( 1 ).toString();
7482
7483 bool foundLayer = false;
7484
7485 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7486 values.at( 0 ),
7487 context,
7488 parent,
7489 [parent, uriPart]( QgsMapLayer *layer ) -> QVariant {
7490 if ( !layer->dataProvider() )
7491 {
7492 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7493 return QVariant();
7494 }
7495
7496 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7497
7498 if ( !uriPart.isNull() )
7499 {
7500 return decodedUri.value( uriPart );
7501 }
7502 else
7503 {
7504 return decodedUri;
7505 }
7506 },
7507 foundLayer
7508 );
7509
7510 if ( !foundLayer )
7511 {
7512 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7513 return QVariant();
7514 }
7515 else
7516 {
7517 return res;
7518 }
7519}
7520
7521static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7522{
7523 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7524 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7525
7526 bool foundLayer = false;
7527 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7528 values.at( 0 ),
7529 context,
7530 parent,
7531 [parent, band, layerProperty]( QgsMapLayer *layer ) -> QVariant {
7532 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7533 if ( !rl )
7534 return QVariant();
7535
7536 if ( band < 1 || band > rl->bandCount() )
7537 {
7538 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7539 return QVariant();
7540 }
7541
7543
7544 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7546 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7548 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7550 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7552 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7554 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7556 else
7557 {
7558 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7559 return QVariant();
7560 }
7561
7562 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7563 switch ( stat )
7564 {
7566 return stats.mean;
7568 return stats.stdDev;
7570 return stats.minimumValue;
7572 return stats.maximumValue;
7574 return stats.range;
7576 return stats.sum;
7577 default:
7578 break;
7579 }
7580 return QVariant();
7581 },
7582 foundLayer
7583 );
7584
7585 if ( !foundLayer )
7586 {
7587#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7588 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7589#endif
7590 return QVariant();
7591 }
7592 else
7593 {
7594 return res;
7595 }
7596}
7597
7598static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7599{
7600 return values;
7601}
7602
7603static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7604{
7605 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7606 bool ascending = values.value( 1 ).toBool();
7607 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7608 return list;
7609}
7610
7611static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7612{
7613 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7614}
7615
7616static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7617{
7618 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7619}
7620
7621static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7622{
7623 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7624}
7625
7626static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7627{
7628 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7629 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7630 int match = 0;
7631 for ( const auto &item : listB )
7632 {
7633 if ( listA.contains( item ) )
7634 match++;
7635 }
7636
7637 return QVariant( match == listB.count() );
7638}
7639
7640static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7641{
7642 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7643}
7644
7645static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7646{
7647 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7648 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7649 if ( pos < list.length() && pos >= 0 )
7650 return list.at( pos );
7651 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7652 return list.at( list.length() + pos );
7653 return QVariant();
7654}
7655
7656static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7657{
7658 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7659 return list.value( 0 );
7660}
7661
7662static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7663{
7664 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7665 return list.value( list.size() - 1 );
7666}
7667
7668static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7669{
7670 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7671 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7672}
7673
7674static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7675{
7676 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7677 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7678}
7679
7680static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7681{
7682 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7683 int i = 0;
7684 double total = 0.0;
7685 for ( const QVariant &item : list )
7686 {
7687 switch ( item.userType() )
7688 {
7689 case QMetaType::Int:
7690 case QMetaType::UInt:
7691 case QMetaType::LongLong:
7692 case QMetaType::ULongLong:
7693 case QMetaType::Float:
7694 case QMetaType::Double:
7695 total += item.toDouble();
7696 ++i;
7697 break;
7698 }
7699 }
7700 return i == 0 ? QVariant() : total / i;
7701}
7702
7703static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7704{
7705 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7706 QVariantList numbers;
7707 for ( const auto &item : list )
7708 {
7709 switch ( item.userType() )
7710 {
7711 case QMetaType::Int:
7712 case QMetaType::UInt:
7713 case QMetaType::LongLong:
7714 case QMetaType::ULongLong:
7715 case QMetaType::Float:
7716 case QMetaType::Double:
7717 numbers.append( item );
7718 break;
7719 }
7720 }
7721 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7722 const int count = numbers.count();
7723 if ( count == 0 )
7724 {
7725 return QVariant();
7726 }
7727 else if ( count % 2 )
7728 {
7729 return numbers.at( count / 2 );
7730 }
7731 else
7732 {
7733 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7734 }
7735}
7736
7737static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7738{
7739 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7740 int i = 0;
7741 double total = 0.0;
7742 for ( const QVariant &item : list )
7743 {
7744 switch ( item.userType() )
7745 {
7746 case QMetaType::Int:
7747 case QMetaType::UInt:
7748 case QMetaType::LongLong:
7749 case QMetaType::ULongLong:
7750 case QMetaType::Float:
7751 case QMetaType::Double:
7752 total += item.toDouble();
7753 ++i;
7754 break;
7755 }
7756 }
7757 return i == 0 ? QVariant() : total;
7758}
7759
7760static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7761{
7762 QVariant result = value;
7763 ( void ) result.convert( static_cast<int>( type ) );
7764 return result;
7765}
7766
7767static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7768{
7769 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7770 QHash< QVariant, int > hash;
7771 for ( const auto &item : list )
7772 {
7773 ++hash[item];
7774 }
7775 const QList< int > occurrences = hash.values();
7776 if ( occurrences.empty() )
7777 return QVariantList();
7778
7779 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7780
7781 const QString option = values.at( 1 ).toString();
7782 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7783 {
7784 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7785 }
7786 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7787 {
7788 if ( hash.isEmpty() )
7789 return QVariant();
7790
7791 return QVariant( hash.key( maxValue ) );
7792 }
7793 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7794 {
7795 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7796 }
7797 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7798 {
7799 if ( maxValue * 2 <= list.size() )
7800 return QVariant();
7801
7802 return QVariant( hash.key( maxValue ) );
7803 }
7804 else
7805 {
7806 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7807 return QVariant();
7808 }
7809}
7810
7811static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7812{
7813 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7814 QHash< QVariant, int > hash;
7815 for ( const auto &item : list )
7816 {
7817 ++hash[item];
7818 }
7819 const QList< int > occurrences = hash.values();
7820 if ( occurrences.empty() )
7821 return QVariantList();
7822
7823 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7824
7825 const QString option = values.at( 1 ).toString();
7826 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7827 {
7828 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7829 }
7830 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7831 {
7832 if ( hash.isEmpty() )
7833 return QVariant();
7834
7835 return QVariant( hash.key( minValue ) );
7836 }
7837 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7838 {
7839 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7840 }
7841 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7842 {
7843 if ( hash.isEmpty() )
7844 return QVariant();
7845
7846 // Remove the majority, all others are minority
7847 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7848 if ( maxValue * 2 > list.size() )
7849 hash.remove( hash.key( maxValue ) );
7850
7851 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7852 }
7853 else
7854 {
7855 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7856 return QVariant();
7857 }
7858}
7859
7860static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7861{
7862 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7863 list.append( values.at( 1 ) );
7864 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7865}
7866
7867static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7868{
7869 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7870 list.prepend( values.at( 1 ) );
7871 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7872}
7873
7874static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7875{
7876 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7877 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7878 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7879}
7880
7881static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7882{
7883 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7884 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7885 if ( position < 0 )
7886 position = position + list.length();
7887 if ( position >= 0 && position < list.length() )
7888 list.removeAt( position );
7889 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7890}
7891
7892static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7893{
7894 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7895 return QVariant();
7896
7897 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7898
7899 const QVariant toRemove = values.at( 1 );
7900 if ( QgsVariantUtils::isNull( toRemove ) )
7901 {
7902 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant &element ) { return QgsVariantUtils::isNull( element ); } ), list.end() );
7903 }
7904 else
7905 {
7906 list.removeAll( toRemove );
7907 }
7908 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7909}
7910
7911static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7912{
7913 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7914 {
7915 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7916
7917 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7918 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7919 {
7920 int index = list.indexOf( it.key() );
7921 while ( index >= 0 )
7922 {
7923 list.replace( index, it.value() );
7924 index = list.indexOf( it.key() );
7925 }
7926 }
7927
7928 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7929 }
7930 else if ( values.count() == 3 )
7931 {
7932 QVariantList before;
7933 QVariantList after;
7934 bool isSingleReplacement = false;
7935
7936 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7937 {
7938 before = QVariantList() << values.at( 1 );
7939 }
7940 else
7941 {
7942 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7943 }
7944
7945 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7946 {
7947 after = QVariantList() << values.at( 2 );
7948 isSingleReplacement = true;
7949 }
7950 else
7951 {
7952 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7953 }
7954
7955 if ( !isSingleReplacement && before.length() != after.length() )
7956 {
7957 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7958 return QVariant();
7959 }
7960
7961 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7962 for ( int i = 0; i < before.length(); i++ )
7963 {
7964 int index = list.indexOf( before.at( i ) );
7965 while ( index >= 0 )
7966 {
7967 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7968 index = list.indexOf( before.at( i ) );
7969 }
7970 }
7971
7972 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7973 }
7974 else
7975 {
7976 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7977 return QVariant();
7978 }
7979}
7980
7981static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7982{
7983 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7984 QVariantList list_new;
7985
7986 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7987 {
7988 while ( list.removeOne( cur ) )
7989 {
7990 list_new.append( cur );
7991 }
7992 }
7993
7994 list_new.append( list );
7995
7996 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7997}
7998
7999static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8000{
8001 QVariantList list;
8002 for ( const QVariant &cur : values )
8003 {
8004 list += QgsExpressionUtils::getListValue( cur, parent );
8005 }
8006 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
8007}
8008
8009static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8010{
8011 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8012 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
8013 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
8014 int slice_length = 0;
8015 // negative positions means positions taken relative to the end of the array
8016 if ( start_pos < 0 )
8017 {
8018 start_pos = list.length() + start_pos;
8019 }
8020 if ( end_pos >= 0 )
8021 {
8022 slice_length = end_pos - start_pos + 1;
8023 }
8024 else
8025 {
8026 slice_length = list.length() + end_pos - start_pos + 1;
8027 }
8028 //avoid negative lengths in QList.mid function
8029 if ( slice_length < 0 )
8030 {
8031 slice_length = 0;
8032 }
8033 list = list.mid( start_pos, slice_length );
8034 return list;
8035}
8036
8037static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8038{
8039 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8040 std::reverse( list.begin(), list.end() );
8041 return list;
8042}
8043
8044static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8045{
8046 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8047 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
8048 for ( const QVariant &cur : array2 )
8049 {
8050 if ( array1.contains( cur ) )
8051 return QVariant( true );
8052 }
8053 return QVariant( false );
8054}
8055
8056static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8057{
8058 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8059
8060 QVariantList distinct;
8061
8062 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8063 {
8064 if ( !distinct.contains( *it ) )
8065 {
8066 distinct += ( *it );
8067 }
8068 }
8069
8070 return distinct;
8071}
8072
8073static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8074{
8075 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8076 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8077 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8078
8079 QString str;
8080
8081 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8082 {
8083 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
8084 if ( it != ( array.constEnd() - 1 ) )
8085 {
8086 str += delimiter;
8087 }
8088 }
8089
8090 return QVariant( str );
8091}
8092
8093static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8094{
8095 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8096 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8097 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8098
8099 QStringList list = str.split( delimiter );
8100 QVariantList array;
8101
8102 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
8103 {
8104 array += ( !( *it ).isEmpty() ) ? *it : empty;
8105 }
8106
8107 return array;
8108}
8109
8110static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8111{
8112 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8113 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
8114 if ( document.isNull() )
8115 return QVariant();
8116
8117 return document.toVariant();
8118}
8119
8120static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8121{
8122 Q_UNUSED( parent )
8123 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
8124 return QString( document.toJson( QJsonDocument::Compact ) );
8125}
8126
8127static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8128{
8129 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8130 if ( str.isEmpty() )
8131 return QVariantMap();
8132 str = str.trimmed();
8133
8134 return QgsHstoreUtils::parse( str );
8135}
8136
8137static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8138{
8139 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8140 return QgsHstoreUtils::build( map );
8141}
8142
8143static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8144{
8145 QVariantMap result;
8146 for ( int i = 0; i + 1 < values.length(); i += 2 )
8147 {
8148 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
8149 }
8150 return result;
8151}
8152
8153static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8154{
8155 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8156 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8157 QVariantMap resultMap;
8158
8159 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8160 {
8161 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
8162 }
8163
8164 return resultMap;
8165}
8166
8167static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8168{
8169 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
8170}
8171
8172static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8173{
8174 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
8175}
8176
8177static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8178{
8179 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8180 map.remove( values.at( 1 ).toString() );
8181 return map;
8182}
8183
8184static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8185{
8186 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8187 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
8188 return map;
8189}
8190
8191static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8192{
8193 QVariantMap result;
8194 for ( const QVariant &cur : values )
8195 {
8196 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
8197 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
8198 result.insert( it.key(), it.value() );
8199 }
8200 return result;
8201}
8202
8203static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8204{
8205 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
8206}
8207
8208static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8209{
8210 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
8211}
8212
8213static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8214{
8215 const QString envVarName = values.at( 0 ).toString();
8216 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
8217 return QVariant();
8218
8219 return QProcessEnvironment::systemEnvironment().value( envVarName );
8220}
8221
8222static QVariant fcnBaseFileName( 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( "base_file_name"_L1 ) );
8228 return QVariant();
8229 }
8230 return QFileInfo( file ).completeBaseName();
8231}
8232
8233static QVariant fcnFileSuffix( 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( "file_suffix"_L1 ) );
8239 return QVariant();
8240 }
8241 return QFileInfo( file ).completeSuffix();
8242}
8243
8244static QVariant fcnFileExists( 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( "file_exists"_L1 ) );
8250 return QVariant();
8251 }
8252 return QFileInfo::exists( file );
8253}
8254
8255static QVariant fcnFileName( 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_name"_L1 ) );
8261 return QVariant();
8262 }
8263 return QFileInfo( file ).fileName();
8264}
8265
8266static QVariant fcnPathIsFile( 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( "is_file"_L1 ) );
8272 return QVariant();
8273 }
8274 return QFileInfo( file ).isFile();
8275}
8276
8277static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8278{
8279 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8280 if ( parent->hasEvalError() )
8281 {
8282 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8283 return QVariant();
8284 }
8285 return QFileInfo( file ).isDir();
8286}
8287
8288static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8289{
8290 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8291 if ( parent->hasEvalError() )
8292 {
8293 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8294 return QVariant();
8295 }
8296 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8297}
8298
8299static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8300{
8301 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8302 if ( parent->hasEvalError() )
8303 {
8304 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8305 return QVariant();
8306 }
8307 return QFileInfo( file ).size();
8308}
8309
8310static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8311{
8312 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8313}
8314
8315static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8316{
8317 QVariant hash;
8318 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8319 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8320
8321 if ( method == "md4"_L1 )
8322 {
8323 hash = fcnHash( str, QCryptographicHash::Md4 );
8324 }
8325 else if ( method == "md5"_L1 )
8326 {
8327 hash = fcnHash( str, QCryptographicHash::Md5 );
8328 }
8329 else if ( method == "sha1"_L1 )
8330 {
8331 hash = fcnHash( str, QCryptographicHash::Sha1 );
8332 }
8333 else if ( method == "sha224"_L1 )
8334 {
8335 hash = fcnHash( str, QCryptographicHash::Sha224 );
8336 }
8337 else if ( method == "sha256"_L1 )
8338 {
8339 hash = fcnHash( str, QCryptographicHash::Sha256 );
8340 }
8341 else if ( method == "sha384"_L1 )
8342 {
8343 hash = fcnHash( str, QCryptographicHash::Sha384 );
8344 }
8345 else if ( method == "sha512"_L1 )
8346 {
8347 hash = fcnHash( str, QCryptographicHash::Sha512 );
8348 }
8349 else if ( method == "sha3_224"_L1 )
8350 {
8351 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8352 }
8353 else if ( method == "sha3_256"_L1 )
8354 {
8355 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8356 }
8357 else if ( method == "sha3_384"_L1 )
8358 {
8359 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8360 }
8361 else if ( method == "sha3_512"_L1 )
8362 {
8363 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8364 }
8365 else if ( method == "keccak_224"_L1 )
8366 {
8367 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8368 }
8369 else if ( method == "keccak_256"_L1 )
8370 {
8371 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8372 }
8373 else if ( method == "keccak_384"_L1 )
8374 {
8375 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8376 }
8377 else if ( method == "keccak_512"_L1 )
8378 {
8379 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8380 }
8381 else
8382 {
8383 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8384 }
8385 return hash;
8386}
8387
8388static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8389{
8390 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8391}
8392
8393static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8394{
8395 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8396}
8397
8398static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8399{
8400 const QByteArray input = values.at( 0 ).toByteArray();
8401 return QVariant( QString( input.toBase64() ) );
8402}
8403
8404static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8405{
8406 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8407 QUrlQuery query;
8408 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8409 {
8410 query.addQueryItem( it.key(), it.value().toString() );
8411 }
8412 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8414
8415static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8416{
8417 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8418 const QByteArray base64 = value.toLocal8Bit();
8419 const QByteArray decoded = QByteArray::fromBase64( base64 );
8420 return QVariant( decoded );
8421}
8422
8424typedef std::function<bool( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend )> RelationFunction;
8425
8426static QVariant executeGeomOverlay(
8427 const QVariantList &values,
8428 const QgsExpressionContext *context,
8429 QgsExpression *parent,
8430 const RelationFunction &relationFunction,
8431 bool invert = false,
8432 double bboxGrow = 0,
8433 bool isNearestFunc = false,
8434 bool isIntersectsFunc = false
8435)
8436{
8437 if ( !context )
8438 {
8439 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8440 return QVariant();
8441 }
8442
8443 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8444 // TODO this function is NOT thread safe
8446 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8448
8449 QgsFeatureRequest request;
8450 request.setTimeout( 10000 );
8451 request.setRequestMayBeNested( true );
8452 request.setFeedback( context->feedback() );
8453
8454 // First parameter is the overlay layer
8455 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8457
8458 const bool layerCanBeCached = node->isStatic( parent, context );
8459 QVariant targetLayerValue = node->eval( parent, context );
8461
8462 // Second parameter is the expression to evaluate (or null for testonly)
8463 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8465 QString subExpString = node->dump();
8466
8467 bool testOnly = ( subExpString == "NULL" );
8468 // TODO this function is NOT thread safe
8470 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8472 if ( !targetLayer ) // No layer, no joy
8473 {
8474 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8475 return QVariant();
8476 }
8477
8478 // Third parameter is the filtering expression
8479 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8481 QString filterString = node->dump();
8482 if ( filterString != "NULL" )
8483 {
8484 request.setFilterExpression( filterString ); //filter cached features
8485 }
8486
8487 // Fourth parameter is the limit
8488 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8490 QVariant limitValue = node->eval( parent, context );
8492 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8493
8494 double max_distance = 0;
8495 bool cacheEnabled = false;
8496
8497 double minOverlap { -1 };
8498 double minInscribedCircleRadius { -1 };
8499 bool returnDetails = false; //#spellok
8500 bool sortByMeasure = false;
8501 bool sortAscending = false;
8502 bool requireMeasures = false;
8503 bool overlapOrRadiusFilter = false;
8504
8506
8507 if ( isNearestFunc ) //maxdistance param handling
8508 {
8509 // Fifth parameter (for nearest only) is the max distance
8510 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8512 QVariant distanceValue = node->eval( parent, context );
8514 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8515
8516 // Sixth (for nearest only) parameter is the cache toggle
8517 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
8519 QVariant cacheValue = node->eval( parent, context );
8521 cacheEnabled = cacheValue.toBool();
8522 }
8523 else
8524 {
8525 // Fifth parameter is the cache toggle
8526 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8528 QVariant cacheValue = node->eval( parent, context );
8530 cacheEnabled = cacheValue.toBool();
8531
8532 // Sixth parameter is the min overlap (area or length)
8533 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8535 const QVariant minOverlapValue = node->eval( parent, context );
8537 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8538
8539 // Seventh parameter is the min inscribed circle radius
8540 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8542 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8544 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8545
8546 // Eighth parameter is the return_details
8547 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8548 // Return measures is only effective when an expression is set
8549 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8550
8551 // Ninth parameter is the sort_by_intersection_size flag
8552 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8553 // Sort by measures is only effective when an expression is set
8554 const QString sorting { node->eval( parent, context ).toString().toLower() };
8555 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8556 sortAscending = sorting.startsWith( "asc" );
8557 requireMeasures = sortByMeasure || returnDetails; //#spellok
8558 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8559
8560 // Tenth parameter is the geometry backend
8561 node = QgsExpressionUtils::getNode( values.at( 9 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8563 const QString backendStr = node->eval( parent, context ).toString().toUpper();
8565
8566 bool ok;
8567 backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
8568 if ( !ok )
8569 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
8570 }
8571
8572 FEAT_FROM_CONTEXT( context, feat )
8573 const QgsGeometry geometry = feat.geometry();
8574
8575 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8576 {
8577 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8578 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8579 }
8580
8581 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8582
8583 QgsRectangle intDomain = geometry.boundingBox();
8584 if ( bboxGrow != 0 )
8585 {
8586 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8587 }
8588
8589 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8590
8591 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8592 // Otherwise, it can be toggled by the user
8593 QgsSpatialIndex spatialIndex;
8594 QgsVectorLayer *cachedTarget;
8595 QList<QgsFeature> features;
8596 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8597 {
8598 // If the cache (local spatial index) is enabled, we materialize the whole
8599 // layer, then do the request on that layer instead.
8600 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8601 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8602
8603 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8604 {
8605 cachedTarget = targetLayer->materialize( request );
8606 if ( layerCanBeCached )
8607 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8608 }
8609 else
8610 {
8611 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8612 }
8613
8614 if ( !context->hasCachedValue( cacheIndex ) )
8615 {
8616 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8617 if ( layerCanBeCached )
8618 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8619 }
8620 else
8621 {
8622 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8623 }
8624
8625 QList<QgsFeatureId> fidsList;
8626 if ( isNearestFunc )
8627 {
8628 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8629 }
8630 else
8631 {
8632 fidsList = spatialIndex.intersects( intDomain );
8633 }
8634
8635 QListIterator<QgsFeatureId> i( fidsList );
8636 while ( i.hasNext() )
8637 {
8638 QgsFeatureId fId2 = i.next();
8639 if ( sameLayers && feat.id() == fId2 )
8640 continue;
8641 features.append( cachedTarget->getFeature( fId2 ) );
8642 }
8643 }
8644 else
8645 {
8646 // If the cache (local spatial index) is not enabled, we directly
8647 // get the features from the target layer
8648 request.setFilterRect( intDomain );
8649 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8650 QgsFeature feat2;
8651 while ( fit.nextFeature( feat2 ) )
8652 {
8653 if ( sameLayers && feat.id() == feat2.id() )
8654 continue;
8655 features.append( feat2 );
8656 }
8657 }
8658
8659 QgsExpression subExpression;
8660 QgsExpressionContext subContext;
8661 if ( !testOnly )
8662 {
8663 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8664 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8665
8666 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8667 {
8668 subExpression = QgsExpression( subExpString );
8670 subExpression.prepare( &subContext );
8671 }
8672 else
8673 {
8674 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8675 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8676 }
8677 }
8678
8679 // //////////////////////////////////////////////////////////////////
8680 // Helper functions for geometry tests
8681
8682 // Test function for linestring geometries, returns TRUE if test passes
8683 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double &overlapValue ) -> bool {
8684 bool testResult { false };
8685 // For return measures:
8686 QVector<double> overlapValues;
8687 const QgsGeometry merged { intersection.mergeLines() };
8688 for ( auto it = merged.const_parts_begin(); !testResult && it != merged.const_parts_end(); ++it )
8689 {
8691 // Check min overlap for intersection (if set)
8692 if ( minOverlap != -1 || requireMeasures )
8693 {
8694 overlapValue = geom->length();
8695 overlapValues.append( overlapValue );
8696 if ( minOverlap != -1 )
8697 {
8698 if ( overlapValue >= minOverlap )
8699 {
8700 testResult = true;
8701 }
8702 else
8703 {
8704 continue;
8705 }
8706 }
8707 }
8708 }
8709
8710 if ( !overlapValues.isEmpty() )
8711 {
8712 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8713 }
8714
8715 return testResult;
8716 };
8717
8718 // Test function for polygon geometries, returns TRUE if test passes
8719 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double &radiusValue, double &overlapValue ) -> bool {
8720 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8721 bool testResult { false };
8722 // For return measures:
8723 QVector<double> overlapValues;
8724 QVector<double> radiusValues;
8725 for ( auto it = intersection.const_parts_begin(); ( !testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8726 {
8728 // Check min overlap for intersection (if set)
8729 if ( minOverlap != -1 || requireMeasures )
8730 {
8731 overlapValue = geom->area();
8732 overlapValues.append( geom->area() );
8733 if ( minOverlap != -1 )
8734 {
8735 if ( overlapValue >= minOverlap )
8736 {
8737 testResult = true;
8738 }
8739 else
8740 {
8741 continue;
8742 }
8743 }
8744 }
8745
8746 // Check min inscribed circle radius for intersection (if set)
8747 if ( minInscribedCircleRadius != -1 || requireMeasures )
8748 {
8749 const QgsRectangle bbox = geom->boundingBox();
8750 const double width = bbox.width();
8751 const double height = bbox.height();
8752 const double size = width > height ? width : height;
8753 const double tolerance = size / 100.0;
8754 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8755 testResult = radiusValue >= minInscribedCircleRadius;
8756 radiusValues.append( radiusValues );
8757 }
8758 } // end for parts
8759
8760 // Get the max values
8761 if ( !radiusValues.isEmpty() )
8762 {
8763 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8764 }
8765
8766 if ( !overlapValues.isEmpty() )
8767 {
8768 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8769 }
8770
8771 return testResult;
8772 };
8773
8774
8775 bool found = false;
8776 int foundCount = 0;
8777 QVariantList results;
8778
8779 QListIterator<QgsFeature> i( features );
8780 try
8781 {
8782 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8783 {
8784 QgsFeature feat2 = i.next();
8785
8786
8787 if ( relationFunction( geometry, feat2.geometry(), values, backend ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8788 {
8789 double overlapValue = -1;
8790 double radiusValue = -1;
8791
8792 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8793 {
8794 QgsGeometry intersection { geometry.intersection( feat2.geometry(), QgsGeometryParameters() ) };
8795
8796 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8797 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8798 {
8799 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8800 intersection = QgsGeometry();
8801 QgsMultiPolygonXY poly;
8802 QgsMultiPolylineXY line;
8803 QgsMultiPointXY point;
8804 for ( const auto &geom : std::as_const( geometries ) )
8805 {
8806 switch ( geom.type() )
8807 {
8809 {
8810 poly.append( geom.asPolygon() );
8811 break;
8812 }
8814 {
8815 line.append( geom.asPolyline() );
8816 break;
8817 }
8819 {
8820 point.append( geom.asPoint() );
8821 break;
8822 }
8825 {
8826 break;
8827 }
8828 }
8829 }
8830
8831 switch ( geometry.type() )
8832 {
8834 {
8835 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8836 break;
8837 }
8839 {
8840 intersection = QgsGeometry::fromMultiPolylineXY( line );
8841 break;
8842 }
8844 {
8845 intersection = QgsGeometry::fromMultiPointXY( point );
8846 break;
8847 }
8850 {
8851 break;
8852 }
8853 }
8854 }
8855
8856 // Depending on the intersection geometry type and on the geometry type of
8857 // the tested geometry we can run different tests and collect different measures
8858 // that can be used for sorting (if required).
8859 switch ( intersection.type() )
8860 {
8862 {
8863 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8864 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8865
8866 if ( !testResult && overlapOrRadiusFilter )
8867 {
8868 continue;
8869 }
8870
8871 break;
8872 }
8873
8875 {
8876 // If the intersection is a linestring and a minimum circle is required
8877 // we can discard this result immediately.
8878 if ( minInscribedCircleRadius != -1 )
8879 {
8880 continue;
8881 }
8882
8883 // Otherwise a test for the overlap value is performed.
8884 const bool testResult { testLinestring( intersection, overlapValue ) };
8885
8886 if ( !testResult && overlapOrRadiusFilter )
8887 {
8888 continue;
8889 }
8890
8891 break;
8892 }
8893
8895 {
8896 // If the intersection is a point and a minimum circle is required
8897 // we can discard this result immediately.
8898 if ( minInscribedCircleRadius != -1 )
8899 {
8900 continue;
8901 }
8902
8903 bool testResult { false };
8904 if ( minOverlap != -1 || requireMeasures )
8905 {
8906 // Initially set this to 0 because it's a point intersection...
8907 overlapValue = 0;
8908 // ... but if the target geometry is not a point and the source
8909 // geometry is a point, we must record the length or the area
8910 // of the intersected geometry and use that as a measure for
8911 // sorting or reporting.
8912 if ( geometry.type() == Qgis::GeometryType::Point )
8913 {
8914 switch ( feat2.geometry().type() )
8915 {
8919 {
8920 break;
8921 }
8923 {
8924 testResult = testLinestring( feat2.geometry(), overlapValue );
8925 break;
8926 }
8928 {
8929 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8930 break;
8931 }
8932 }
8933 }
8934
8935 if ( !testResult && overlapOrRadiusFilter )
8936 {
8937 continue;
8938 }
8939 }
8940 break;
8941 }
8942
8945 {
8946 continue;
8947 }
8948 }
8949 }
8950
8951 found = true;
8952 foundCount++;
8953
8954 // We just want a single boolean result if there is any intersect: finish and return true
8955 if ( testOnly )
8956 break;
8957
8958 if ( !invert )
8959 {
8960 // We want a list of attributes / geometries / other expression values, evaluate now
8961 subContext.setFeature( feat2 );
8962 const QVariant expResult = subExpression.evaluate( &subContext );
8963
8964 if ( requireMeasures )
8965 {
8966 QVariantMap resultRecord;
8967 resultRecord.insert( u"id"_s, feat2.id() );
8968 resultRecord.insert( u"result"_s, expResult );
8969 // Overlap is always added because return measures was set
8970 resultRecord.insert( u"overlap"_s, overlapValue );
8971 // Radius is only added when is different than -1 (because for linestrings is not set)
8972 if ( radiusValue != -1 )
8973 {
8974 resultRecord.insert( u"radius"_s, radiusValue );
8975 }
8976 results.append( resultRecord );
8977 }
8978 else
8979 {
8980 results.append( expResult );
8981 }
8982 }
8983 else
8984 {
8985 // If not, results is a list of found ids, which we'll inverse and evaluate below
8986 results.append( feat2.id() );
8987 }
8988 }
8989 }
8990 }
8991 catch ( QgsException &e )
8992 {
8993 parent->setEvalErrorString( e.what() );
8994 return false;
8995 }
8996
8997 if ( testOnly )
8998 {
8999 if ( invert )
9000 found = !found; //for disjoint condition
9001 return found;
9002 }
9003
9004 if ( !invert )
9005 {
9006 if ( requireMeasures )
9007 {
9008 if ( sortByMeasure )
9009 {
9010 std::sort( results.begin(), results.end(), [sortAscending]( const QVariant &recordA, const QVariant &recordB ) -> bool {
9011 return sortAscending ? recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
9012 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
9013 } );
9014 }
9015 // Resize
9016 if ( limit > 0 && results.size() > limit )
9017 {
9018 results.erase( results.begin() + limit );
9019 }
9020
9021 if ( !returnDetails ) //#spellok
9022 {
9023 QVariantList expResults;
9024 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
9025 {
9026 expResults.append( it->toMap().value( u"result"_s ) );
9027 }
9028 return expResults;
9029 }
9030 }
9031
9032 return results;
9033 }
9034
9035 // for disjoint condition returns the results for cached layers not intersected feats
9036 QVariantList disjoint_results;
9037 QgsFeature feat2;
9038 QgsFeatureRequest request2;
9039 request2.setLimit( limit );
9040 if ( context )
9041 request2.setFeedback( context->feedback() );
9042 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
9043 while ( fi.nextFeature( feat2 ) )
9044 {
9045 if ( !results.contains( feat2.id() ) )
9046 {
9047 subContext.setFeature( feat2 );
9048 disjoint_results.append( subExpression.evaluate( &subContext ) );
9049 }
9050 }
9051 return disjoint_results;
9052}
9053
9054// Intersect functions:
9055
9056static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9057{
9058 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9059 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, false, true );
9060}
9061
9062static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9063{
9064 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.contains( other ); };
9065 return executeGeomOverlay( values, context, parent, geomFunction );
9066}
9067
9068static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9069{
9070 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.crosses( other ); };
9071 return executeGeomOverlay( values, context, parent, geomFunction );
9072}
9073
9074static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9075{
9076 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9077 return geometry.isExactlyEqual( other, Qgis::GeometryBackend::QGIS );
9078 };
9079 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9080}
9081
9082static QVariant fcnGeomOverlayEqualsExact( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9083{
9084 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9085 return geometry.isExactlyEqual( other, backend );
9086 };
9087 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9088}
9089
9090static QVariant fcnGeomOverlayEqualsTopological( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9091{
9092 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9093 return geometry.isTopologicallyEqual( other, backend );
9094 };
9095 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9096}
9097
9098static QVariant fcnGeomOverlayEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9099{
9100 // This parameter is the epsilon tolerance
9101 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 10 ), parent );
9103 QVariant epsilonValue = node->eval( parent, context );
9105 double epsilon = QgsExpressionUtils::getDoubleValue( epsilonValue, parent );
9106
9107 RelationFunction geomFunction = [epsilon]( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9108 return geometry.isFuzzyEqual( other, epsilon, backend );
9109 };
9110 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9111}
9112
9113static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9114{
9115 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.touches( other ); };
9116 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9117}
9118
9119static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9120{
9121 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.within( other ); };
9122 return executeGeomOverlay( values, context, parent, geomFunction );
9123}
9124
9125static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9126{
9127 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9128 return executeGeomOverlay( values, context, parent, geomFunction, true, 0, false, true );
9129}
9130
9131static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9132{
9133 RelationFunction geomFunction = []( const QgsGeometry &, const QgsGeometry &, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9134 return true; // does nothing on purpose
9135 };
9136 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, true );
9137}
9138
9139const QList<QgsExpressionFunction *> &QgsExpression::Functions()
9140{
9141 // The construction of the list isn't thread-safe, and without the mutex,
9142 // crashes in the WFS provider may occur, since it can parse expressions
9143 // in parallel.
9144 // The mutex needs to be recursive.
9145 QMutexLocker locker( &sFunctionsMutex );
9146
9147 QList<QgsExpressionFunction *> &functions = *sFunctions();
9148
9149 if ( functions.isEmpty() )
9150 {
9152 << QgsExpressionFunction::Parameter( u"expression"_s )
9153 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
9154 << QgsExpressionFunction::Parameter( u"filter"_s, true );
9155
9156 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
9157 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true ) << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9158
9159 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
9160 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9161
9162 functions
9163 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
9164 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
9165 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
9166 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnAzimuth, u"GeometryGroup"_s )
9167 << new QgsStaticExpressionFunction(
9168 u"bearing"_s,
9170 << QgsExpressionFunction::Parameter( u"point1"_s )
9171 << QgsExpressionFunction::Parameter( u"point2"_s )
9172 << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() )
9173 << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ),
9174 fcnBearing,
9175 u"GeometryGroup"_s
9176 )
9177 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnInclination, u"GeometryGroup"_s )
9178 << new QgsStaticExpressionFunction(
9179 u"project"_s,
9181 << QgsExpressionFunction::Parameter( u"point"_s )
9182 << QgsExpressionFunction::Parameter( u"distance"_s )
9183 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9184 << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ),
9185 fcnProject,
9186 u"GeometryGroup"_s
9187 )
9188 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
9189 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
9190 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
9191 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
9192 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
9193 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
9194 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
9195 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
9196 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
9197 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
9198 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
9199 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
9200 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
9201
9202 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction(
9203 u"rand"_s,
9204 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9205 fcnRnd,
9206 u"Math"_s
9207 );
9208 randFunc->setIsStatic( false );
9209 functions << randFunc;
9210
9211 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction(
9212 u"randf"_s,
9214 << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 )
9215 << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 )
9216 << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9217 fcnRndF,
9218 u"Math"_s
9219 );
9220 randfFunc->setIsStatic( false );
9221 functions << randfFunc;
9222
9223 functions
9224 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9225 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9226 << 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 )
9227 << new QgsStaticExpressionFunction(
9228 u"scale_linear"_s,
9230 << QgsExpressionFunction::Parameter( u"value"_s )
9231 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9232 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9233 << QgsExpressionFunction::Parameter( u"range_min"_s )
9234 << QgsExpressionFunction::Parameter( u"range_max"_s ),
9235 fcnLinearScale,
9236 u"Math"_s
9237 )
9238 << new QgsStaticExpressionFunction(
9239 u"scale_polynomial"_s,
9241 << QgsExpressionFunction::Parameter( u"value"_s )
9242 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9243 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9244 << QgsExpressionFunction::Parameter( u"range_min"_s )
9245 << QgsExpressionFunction::Parameter( u"range_max"_s )
9246 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9247 fcnPolynomialScale,
9248 u"Math"_s,
9249 QString(),
9250 false,
9251 QSet<QString>(),
9252 false,
9253 QStringList() << u"scale_exp"_s
9254 )
9255 << new QgsStaticExpressionFunction(
9256 u"scale_exponential"_s,
9258 << QgsExpressionFunction::Parameter( u"value"_s )
9259 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9260 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9261 << QgsExpressionFunction::Parameter( u"range_min"_s )
9262 << QgsExpressionFunction::Parameter( u"range_max"_s )
9263 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9264 fcnExponentialScale,
9265 u"Math"_s
9266 )
9267 << new QgsStaticExpressionFunction(
9268 u"scale_cubic_bezier"_s,
9270 << QgsExpressionFunction::Parameter( u"value"_s )
9271 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9272 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9273 << QgsExpressionFunction::Parameter( u"range_min"_s )
9274 << QgsExpressionFunction::Parameter( u"range_max"_s )
9275 << QgsExpressionFunction::Parameter( u"x1"_s )
9276 << QgsExpressionFunction::Parameter( u"y1"_s )
9277 << QgsExpressionFunction::Parameter( u"x2"_s )
9278 << QgsExpressionFunction::Parameter( u"y2"_s ),
9279 fcnCubicBezierScale,
9280 u"Math"_s
9281 )
9282 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
9283 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
9284 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
9285 << 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 )
9286 << 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 )
9287 << 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 )
9288 << new QgsStaticExpressionFunction(
9289 u"to_string"_s,
9290 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9291 fcnToString,
9292 QStringList() << u"Conversions"_s << u"String"_s,
9293 QString(),
9294 false,
9295 QSet<QString>(),
9296 false,
9297 QStringList() << u"tostring"_s
9298 )
9299 << new QgsStaticExpressionFunction(
9300 u"to_datetime"_s,
9302 << QgsExpressionFunction::Parameter( u"value"_s )
9303 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9304 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9305 fcnToDateTime,
9306 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9307 QString(),
9308 false,
9309 QSet<QString>(),
9310 false,
9311 QStringList() << u"todatetime"_s
9312 )
9313 << new QgsStaticExpressionFunction(
9314 u"to_date"_s,
9316 << QgsExpressionFunction::Parameter( u"value"_s )
9317 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9318 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9319 fcnToDate,
9320 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9321 QString(),
9322 false,
9323 QSet<QString>(),
9324 false,
9325 QStringList() << u"todate"_s
9326 )
9327 << new QgsStaticExpressionFunction(
9328 u"to_time"_s,
9330 << QgsExpressionFunction::Parameter( u"value"_s )
9331 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9332 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9333 fcnToTime,
9334 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9335 QString(),
9336 false,
9337 QSet<QString>(),
9338 false,
9339 QStringList() << u"totime"_s
9340 )
9341 << new QgsStaticExpressionFunction(
9342 u"to_interval"_s,
9343 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9344 fcnToInterval,
9345 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9346 QString(),
9347 false,
9348 QSet<QString>(),
9349 false,
9350 QStringList() << u"tointerval"_s
9351 )
9352 << new QgsStaticExpressionFunction(
9353 u"to_dm"_s,
9355 << QgsExpressionFunction::Parameter( u"value"_s )
9356 << QgsExpressionFunction::Parameter( u"axis"_s )
9357 << QgsExpressionFunction::Parameter( u"precision"_s )
9358 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9359 fcnToDegreeMinute,
9360 u"Conversions"_s,
9361 QString(),
9362 false,
9363 QSet<QString>(),
9364 false,
9365 QStringList() << u"todm"_s
9366 )
9367 << new QgsStaticExpressionFunction(
9368 u"to_dms"_s,
9370 << QgsExpressionFunction::Parameter( u"value"_s )
9371 << QgsExpressionFunction::Parameter( u"axis"_s )
9372 << QgsExpressionFunction::Parameter( u"precision"_s )
9373 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9374 fcnToDegreeMinuteSecond,
9375 u"Conversions"_s,
9376 QString(),
9377 false,
9378 QSet<QString>(),
9379 false,
9380 QStringList() << u"todms"_s
9381 )
9382 << 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 )
9383 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
9384 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
9385 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
9386 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9387 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
9388 << new QgsStaticExpressionFunction(
9389 u"if"_s,
9391 << QgsExpressionFunction::Parameter( u"condition"_s )
9392 << QgsExpressionFunction::Parameter( u"result_when_true"_s )
9393 << QgsExpressionFunction::Parameter( u"result_when_false"_s ),
9394 fcnIf,
9395 u"Conditionals"_s,
9396 QString(),
9397 false,
9398 QSet<QString>(),
9399 true
9400 )
9401 << new QgsStaticExpressionFunction(
9402 u"try"_s,
9403 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ),
9404 fcnTry,
9405 u"Conditionals"_s,
9406 QString(),
9407 false,
9408 QSet<QString>(),
9409 true
9410 )
9411
9412 << new QgsStaticExpressionFunction(
9413 u"aggregate"_s,
9415 << QgsExpressionFunction::Parameter( u"layer"_s )
9416 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9417 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9418 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9419 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9420 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9421 fcnAggregate,
9422 u"Aggregates"_s,
9423 QString(),
9424 []( const QgsExpressionNodeFunction *node ) {
9425 // usesGeometry callback: return true if @parent variable is referenced
9426
9427 if ( !node )
9428 return true;
9429
9430 if ( !node->args() )
9431 return false;
9432
9433 QSet<QString> referencedVars;
9434 if ( node->args()->count() > 2 )
9435 {
9436 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9437 referencedVars = subExpressionNode->referencedVariables();
9438 }
9439
9440 if ( node->args()->count() > 3 )
9441 {
9442 QgsExpressionNode *filterNode = node->args()->at( 3 );
9443 referencedVars.unite( filterNode->referencedVariables() );
9444 }
9445 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
9446 },
9447 []( const QgsExpressionNodeFunction *node ) {
9448 // referencedColumns callback: return AllAttributes if @parent variable is referenced
9449
9450 if ( !node )
9451 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9452
9453 if ( !node->args() )
9454 return QSet<QString>();
9455
9456 QSet<QString> referencedCols;
9457 QSet<QString> referencedVars;
9458
9459 if ( node->args()->count() > 2 )
9460 {
9461 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9462 referencedVars = subExpressionNode->referencedVariables();
9463 referencedCols = subExpressionNode->referencedColumns();
9464 }
9465 if ( node->args()->count() > 3 )
9466 {
9467 QgsExpressionNode *filterNode = node->args()->at( 3 );
9468 referencedVars = filterNode->referencedVariables();
9469 referencedCols.unite( filterNode->referencedColumns() );
9470 }
9471
9472 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
9473 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9474 else
9475 return referencedCols;
9476 },
9477 true
9478 )
9479
9480 << new QgsStaticExpressionFunction(
9481 u"relation_aggregate"_s,
9483 << QgsExpressionFunction::Parameter( u"relation"_s )
9484 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9485 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9486 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9487 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9488 fcnAggregateRelation,
9489 u"Aggregates"_s,
9490 QString(),
9491 false,
9492 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9493 true
9494 )
9495
9496 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9497 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9498 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9499 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9500 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9501 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9502 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9503 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9504 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9505 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9506 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9507 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9508 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9509 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9510 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9511 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9512 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9513 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9514 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9515 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9516 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9517
9518 << 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 )
9519 << new QgsStaticExpressionFunction(
9520 u"regexp_matches"_s,
9522 << QgsExpressionFunction::Parameter( u"string"_s )
9523 << QgsExpressionFunction::Parameter( u"regex"_s )
9524 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
9525 fcnRegexpMatches,
9526 u"Arrays"_s
9527 )
9528
9529 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9530 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s ) << QgsExpressionFunction::Parameter( u"datetime2"_s ), fcnAge, u"Date and Time"_s )
9531 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9532 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9533 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9534 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9535 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9536 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9537 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9538 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9539 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9540 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9541 << 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 )
9542 << new QgsStaticExpressionFunction(
9543 u"make_time"_s,
9544 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s ) << QgsExpressionFunction::Parameter( u"minute"_s ) << QgsExpressionFunction::Parameter( u"second"_s ),
9545 fcnMakeTime,
9546 u"Date and Time"_s
9547 )
9548 << new QgsStaticExpressionFunction(
9549 u"make_datetime"_s,
9551 << QgsExpressionFunction::Parameter( u"year"_s )
9552 << QgsExpressionFunction::Parameter( u"month"_s )
9553 << QgsExpressionFunction::Parameter( u"day"_s )
9554 << QgsExpressionFunction::Parameter( u"hour"_s )
9555 << QgsExpressionFunction::Parameter( u"minute"_s )
9556 << QgsExpressionFunction::Parameter( u"second"_s ),
9557 fcnMakeDateTime,
9558 u"Date and Time"_s
9559 )
9560 << new QgsStaticExpressionFunction(
9561 u"make_interval"_s,
9563 << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9564 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9565 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9566 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9567 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9568 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9569 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9570 fcnMakeInterval,
9571 u"Date and Time"_s
9572 )
9573 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9574 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9575 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9576 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9577 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9578 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9579 << new QgsStaticExpressionFunction(
9580 u"substr_count"_s,
9582 << QgsExpressionFunction::Parameter( u"string"_s )
9583 << QgsExpressionFunction::Parameter( u"substring"_s )
9584 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9585 fcnSubstrCount,
9586 u"String"_s
9587 )
9588 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9589 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9590 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9591 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9592 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9593 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9594 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9595 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9596 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9597 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9598 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9599 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9600 << new QgsStaticExpressionFunction(
9601 u"wordwrap"_s,
9603 << QgsExpressionFunction::Parameter( u"text"_s )
9604 << QgsExpressionFunction::Parameter( u"length"_s )
9605 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ),
9606 fcnWordwrap,
9607 u"String"_s
9608 )
9609 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9610 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9611 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s ) }, fcnRepeat, u"String"_s )
9612 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9613 << new QgsStaticExpressionFunction(
9614 u"regexp_replace"_s,
9615 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"replacement"_s ),
9616 fcnRegexpReplace,
9617 u"String"_s
9618 )
9619 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9620 << new QgsStaticExpressionFunction(
9621 u"substr"_s,
9622 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ),
9623 fcnSubstr,
9624 u"String"_s,
9625 QString(),
9626 false,
9627 QSet< QString >(),
9628 false,
9629 QStringList(),
9630 true
9631 )
9632 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9633 << new QgsStaticExpressionFunction( u"concat_ws"_s, -1, fcnConcatWs, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9634 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9635 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9636 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9637 << 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 )
9638 << 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 )
9639 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9640 << new QgsStaticExpressionFunction(
9641 u"format_number"_s,
9643 << QgsExpressionFunction::Parameter( u"number"_s )
9644 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9645 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9646 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9647 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ),
9648 fcnFormatNumber,
9649 u"String"_s
9650 )
9651 << new QgsStaticExpressionFunction(
9652 u"format_date"_s,
9654 << QgsExpressionFunction::Parameter( u"datetime"_s )
9655 << QgsExpressionFunction::Parameter( u"format"_s )
9656 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9657 fcnFormatDate,
9658 QStringList() << u"String"_s << u"Date and Time"_s
9659 )
9660 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9661 << new QgsStaticExpressionFunction(
9662 u"color_mix_rgb"_s,
9663 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9664 fcnColorMixRgb,
9665 u"Color"_s
9666 )
9667 << new QgsStaticExpressionFunction(
9668 u"color_mix"_s,
9669 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9670 fcnColorMix,
9671 u"Color"_s
9672 )
9673 << 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 )
9674 << new QgsStaticExpressionFunction(
9675 u"color_rgbf"_s,
9677 << QgsExpressionFunction::Parameter( u"red"_s )
9678 << QgsExpressionFunction::Parameter( u"green"_s )
9679 << QgsExpressionFunction::Parameter( u"blue"_s )
9680 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9681 fcnColorRgbF,
9682 u"Color"_s
9683 )
9684 << new QgsStaticExpressionFunction(
9685 u"color_rgba"_s,
9687 << QgsExpressionFunction::Parameter( u"red"_s )
9688 << QgsExpressionFunction::Parameter( u"green"_s )
9689 << QgsExpressionFunction::Parameter( u"blue"_s )
9690 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9691 fncColorRgba,
9692 u"Color"_s
9693 )
9694 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColor, u"Color"_s )
9695 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColorObject, u"Color"_s )
9696 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ), fcnCreateRamp, u"Color"_s )
9697 << new QgsStaticExpressionFunction(
9698 u"color_hsl"_s,
9699 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"lightness"_s ),
9700 fcnColorHsl,
9701 u"Color"_s
9702 )
9703 << new QgsStaticExpressionFunction(
9704 u"color_hsla"_s,
9706 << QgsExpressionFunction::Parameter( u"hue"_s )
9707 << QgsExpressionFunction::Parameter( u"saturation"_s )
9708 << QgsExpressionFunction::Parameter( u"lightness"_s )
9709 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9710 fncColorHsla,
9711 u"Color"_s
9712 )
9713 << new QgsStaticExpressionFunction(
9714 u"color_hslf"_s,
9716 << QgsExpressionFunction::Parameter( u"hue"_s )
9717 << QgsExpressionFunction::Parameter( u"saturation"_s )
9718 << QgsExpressionFunction::Parameter( u"lightness"_s )
9719 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9720 fcnColorHslF,
9721 u"Color"_s
9722 )
9723 << new QgsStaticExpressionFunction(
9724 u"color_hsv"_s,
9725 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9726 fcnColorHsv,
9727 u"Color"_s
9728 )
9729 << new QgsStaticExpressionFunction(
9730 u"color_hsva"_s,
9732 << QgsExpressionFunction::Parameter( u"hue"_s )
9733 << QgsExpressionFunction::Parameter( u"saturation"_s )
9734 << QgsExpressionFunction::Parameter( u"value"_s )
9735 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9736 fncColorHsva,
9737 u"Color"_s
9738 )
9739 << new QgsStaticExpressionFunction(
9740 u"color_hsvf"_s,
9742 << QgsExpressionFunction::Parameter( u"hue"_s )
9743 << QgsExpressionFunction::Parameter( u"saturation"_s )
9744 << QgsExpressionFunction::Parameter( u"value"_s )
9745 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9746 fcnColorHsvF,
9747 u"Color"_s
9748 )
9749 << new QgsStaticExpressionFunction(
9750 u"color_cmyk"_s,
9752 << QgsExpressionFunction::Parameter( u"cyan"_s )
9753 << QgsExpressionFunction::Parameter( u"magenta"_s )
9754 << QgsExpressionFunction::Parameter( u"yellow"_s )
9755 << QgsExpressionFunction::Parameter( u"black"_s ),
9756 fcnColorCmyk,
9757 u"Color"_s
9758 )
9759 << new QgsStaticExpressionFunction(
9760 u"color_cmyka"_s,
9762 << QgsExpressionFunction::Parameter( u"cyan"_s )
9763 << QgsExpressionFunction::Parameter( u"magenta"_s )
9764 << QgsExpressionFunction::Parameter( u"yellow"_s )
9765 << QgsExpressionFunction::Parameter( u"black"_s )
9766 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9767 fncColorCmyka,
9768 u"Color"_s
9769 )
9770 << new QgsStaticExpressionFunction(
9771 u"color_cmykf"_s,
9773 << QgsExpressionFunction::Parameter( u"cyan"_s )
9774 << QgsExpressionFunction::Parameter( u"magenta"_s )
9775 << QgsExpressionFunction::Parameter( u"yellow"_s )
9776 << QgsExpressionFunction::Parameter( u"black"_s )
9777 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9778 fcnColorCmykF,
9779 u"Color"_s
9780 )
9781 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ), fncColorPart, u"Color"_s )
9782 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncDarker, u"Color"_s )
9783 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncLighter, u"Color"_s )
9784 << new QgsStaticExpressionFunction(
9785 u"set_color_part"_s,
9786 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9787 fncSetColorPart,
9788 u"Color"_s
9789 )
9790
9791 // file info
9792 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnBaseFileName, u"Files and Paths"_s )
9793 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSuffix, u"Files and Paths"_s )
9794 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileExists, u"Files and Paths"_s )
9795 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileName, u"Files and Paths"_s )
9796 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsFile, u"Files and Paths"_s )
9797 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsDir, u"Files and Paths"_s )
9798 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFilePath, u"Files and Paths"_s )
9799 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSize, u"Files and Paths"_s )
9800
9801 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ), fcnExif, u"Files and Paths"_s )
9802 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnExifGeoTag, u"GeometryGroup"_s )
9803
9804 // hash
9805 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ), fcnGenericHash, u"Conversions"_s )
9806 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashMd5, u"Conversions"_s )
9807 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashSha256, u"Conversions"_s )
9808
9809 //base64
9810 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBase64, u"Conversions"_s )
9811 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnFromBase64, u"Conversions"_s )
9812
9813 // magnetic models
9814 << new QgsStaticExpressionFunction(
9815 u"magnetic_declination"_s,
9817 << QgsExpressionFunction::Parameter( u"model_name"_s )
9818 << QgsExpressionFunction::Parameter( u"date"_s )
9819 << QgsExpressionFunction::Parameter( u"latitude"_s )
9820 << QgsExpressionFunction::Parameter( u"longitude"_s )
9821 << QgsExpressionFunction::Parameter( u"height"_s )
9822 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9823 fcnMagneticDeclination,
9824 u"MagneticModels"_s
9825 )
9826 << new QgsStaticExpressionFunction(
9827 u"magnetic_inclination"_s,
9829 << QgsExpressionFunction::Parameter( u"model_name"_s )
9830 << QgsExpressionFunction::Parameter( u"date"_s )
9831 << QgsExpressionFunction::Parameter( u"latitude"_s )
9832 << QgsExpressionFunction::Parameter( u"longitude"_s )
9833 << QgsExpressionFunction::Parameter( u"height"_s )
9834 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9835 fcnMagneticInclination,
9836 u"MagneticModels"_s
9837 )
9838 << new QgsStaticExpressionFunction(
9839 u"magnetic_declination_rate_of_change"_s,
9841 << QgsExpressionFunction::Parameter( u"model_name"_s )
9842 << QgsExpressionFunction::Parameter( u"date"_s )
9843 << QgsExpressionFunction::Parameter( u"latitude"_s )
9844 << QgsExpressionFunction::Parameter( u"longitude"_s )
9845 << QgsExpressionFunction::Parameter( u"height"_s )
9846 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9847 fcnMagneticDeclinationRateOfChange,
9848 u"MagneticModels"_s
9849 )
9850 << new QgsStaticExpressionFunction(
9851 u"magnetic_inclination_rate_of_change"_s,
9853 << QgsExpressionFunction::Parameter( u"model_name"_s )
9854 << QgsExpressionFunction::Parameter( u"date"_s )
9855 << QgsExpressionFunction::Parameter( u"latitude"_s )
9856 << QgsExpressionFunction::Parameter( u"longitude"_s )
9857 << QgsExpressionFunction::Parameter( u"height"_s )
9858 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9859 fcnMagneticInclinationRateOfChange,
9860 u"MagneticModels"_s
9861 )
9862
9863 // deprecated stuff - hidden from users
9864 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9865
9866 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9867 geomFunc->setIsStatic( false );
9868 functions << geomFunc;
9869
9870 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9871 areaFunc->setIsStatic( false );
9872 functions << areaFunc;
9873
9874 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9875
9876 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9877 lengthFunc->setIsStatic( false );
9878 functions << lengthFunc;
9879
9880 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9881 perimeterFunc->setIsStatic( false );
9882 functions << perimeterFunc;
9883
9884 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9885
9886 functions << new QgsStaticExpressionFunction( u"roundness"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnRoundness, u"GeometryGroup"_s );
9887
9888 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9889 xFunc->setIsStatic( false );
9890 functions << xFunc;
9891
9892 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9893 yFunc->setIsStatic( false );
9894 functions << yFunc;
9895
9896 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9897 zFunc->setIsStatic( false );
9898 functions << zFunc;
9899
9900 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions {
9901 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9902 { u"overlay_contains"_s, fcnGeomOverlayContains },
9903 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9904 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9905 { u"overlay_equals_exact"_s, fcnGeomOverlayEqualsExact },
9906 { u"overlay_equals_topological"_s, fcnGeomOverlayEqualsTopological },
9907 { u"overlay_equals_fuzzy"_s, fcnGeomOverlayEqualsFuzzy },
9908 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9909 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9910 { u"overlay_within"_s, fcnGeomOverlayWithin },
9911 };
9912 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9913 while ( i.hasNext() )
9914 {
9915 i.next();
9916 QString defaultBackend = i.key() == "overlay_equals"_L1 ? QString( "QGIS" ) : QString( "GEOS" );
9917 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction(
9918 i.key(),
9920 << QgsExpressionFunction::Parameter( u"layer"_s )
9921 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9922 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9923 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9924 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9925 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9926 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9927 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9928 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false )
9929 << QgsExpressionFunction::Parameter( u"backend"_s, true, defaultBackend, false )
9930 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4, false ),
9931 i.value(),
9932 u"GeometryGroup"_s,
9933 QString(),
9934 true,
9935 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9936 true
9937 );
9938
9939 // The current feature is accessed for the geometry, so this should not be cached
9940 fcnGeomOverlayFunc->setIsStatic( false );
9941 functions << fcnGeomOverlayFunc;
9942 }
9943
9944 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction(
9945 u"overlay_nearest"_s,
9947 << QgsExpressionFunction::Parameter( u"layer"_s )
9948 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9949 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9950 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9951 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9952 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9953 fcnGeomOverlayNearest,
9954 u"GeometryGroup"_s,
9955 QString(),
9956 true,
9957 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9958 true
9959 );
9960 // The current feature is accessed for the geometry, so this should not be cached
9961 fcnGeomOverlayNearestFunc->setIsStatic( false );
9962 functions << fcnGeomOverlayNearestFunc;
9963
9964 functions
9965 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9966 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9967 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
9968 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
9969 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
9970 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
9971 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
9972 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
9973 << 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 )
9974 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
9975 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
9976 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
9977 << 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 )
9978 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
9979 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
9980 << new QgsStaticExpressionFunction(
9981 u"make_triangle"_s,
9982 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ) << QgsExpressionFunction::Parameter( u"point3"_s ),
9983 fcnMakeTriangle,
9984 u"GeometryGroup"_s
9985 )
9986 << new QgsStaticExpressionFunction(
9987 u"make_circle"_s,
9989 << QgsExpressionFunction::Parameter( u"center"_s )
9990 << QgsExpressionFunction::Parameter( u"radius"_s )
9991 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9992 fcnMakeCircle,
9993 u"GeometryGroup"_s
9994 )
9995 << new QgsStaticExpressionFunction(
9996 u"make_ellipse"_s,
9998 << QgsExpressionFunction::Parameter( u"center"_s )
9999 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
10000 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
10001 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10002 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
10003 fcnMakeEllipse,
10004 u"GeometryGroup"_s
10005 )
10006 << new QgsStaticExpressionFunction(
10007 u"make_regular_polygon"_s,
10009 << QgsExpressionFunction::Parameter( u"center"_s )
10010 << QgsExpressionFunction::Parameter( u"radius"_s )
10011 << QgsExpressionFunction::Parameter( u"number_sides"_s )
10012 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
10013 fcnMakeRegularPolygon,
10014 u"GeometryGroup"_s
10015 )
10016 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnMakeSquare, u"GeometryGroup"_s )
10017 << new QgsStaticExpressionFunction(
10018 u"make_rectangle_3points"_s,
10020 << QgsExpressionFunction::Parameter( u"point1"_s )
10021 << QgsExpressionFunction::Parameter( u"point2"_s )
10022 << QgsExpressionFunction::Parameter( u"point3"_s )
10023 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
10024 fcnMakeRectangleFrom3Points,
10025 u"GeometryGroup"_s
10026 )
10027 << new QgsStaticExpressionFunction(
10028 u"make_valid"_s,
10030 QgsExpressionFunction::Parameter( u"geometry"_s ),
10031#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
10032 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
10033#else
10034 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
10035#endif
10036 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
10037 },
10038 fcnGeomMakeValid,
10039 u"GeometryGroup"_s
10040 );
10041
10042 functions
10043 << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
10044 functions
10045 << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
10046 functions
10047 << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
10048 functions
10049 << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
10050
10051 QgsStaticExpressionFunction *xAtFunc
10052 = 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 );
10053 xAtFunc->setIsStatic( false );
10054 functions << xAtFunc;
10055
10056
10057 QgsStaticExpressionFunction *yAtFunc
10058 = 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 );
10059 yAtFunc->setIsStatic( false );
10060 functions << yAtFunc;
10061
10062 functions
10063 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
10064 << 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 )
10065 << 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 )
10066 << 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 )
10067 << 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 )
10068 << 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 )
10069 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10070 << 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 )
10071 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
10072 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
10073 << new QgsStaticExpressionFunction(
10074 u"intersects_bbox"_s,
10075 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10076 fcnBbox,
10077 u"GeometryGroup"_s,
10078 QString(),
10079 false,
10080 QSet<QString>(),
10081 false,
10082 QStringList() << u"bbox"_s
10083 )
10084 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDisjoint, u"GeometryGroup"_s )
10085 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersects, u"GeometryGroup"_s )
10086 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnTouches, u"GeometryGroup"_s )
10087 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCrosses, u"GeometryGroup"_s )
10088 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnContains, u"GeometryGroup"_s )
10089 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnOverlaps, u"GeometryGroup"_s )
10090 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnWithin, u"GeometryGroup"_s )
10091 << new QgsStaticExpressionFunction( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnEquals, u"GeometryGroup"_s )
10092 << new QgsStaticExpressionFunction(
10093 u"equals_exact"_s,
10095 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10096 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10097 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s ),
10098 fcnIsEqualsExact,
10099 u"GeometryGroup"_s
10100 )
10101 << new QgsStaticExpressionFunction(
10102 u"equals_topological"_s,
10104 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10105 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10106 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"GEOS"_s ),
10107 fcnIsEqualsTopological,
10108 u"GeometryGroup"_s
10109 )
10110 << new QgsStaticExpressionFunction(
10111 u"equals_fuzzy"_s,
10113 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10114 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10115 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s )
10116 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4 ),
10117 fcnIsEqualsFuzzy,
10118 u"GeometryGroup"_s
10119 )
10120 << 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 )
10121 << new QgsStaticExpressionFunction(
10122 u"rotate"_s,
10124 << QgsExpressionFunction::Parameter( u"geometry"_s )
10125 << QgsExpressionFunction::Parameter( u"rotation"_s )
10126 << QgsExpressionFunction::Parameter( u"center"_s, true )
10127 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
10128 fcnRotate,
10129 u"GeometryGroup"_s
10130 )
10131 << new QgsStaticExpressionFunction(
10132 u"scale"_s,
10134 << QgsExpressionFunction::Parameter( u"geometry"_s )
10135 << QgsExpressionFunction::Parameter( u"x_scale"_s )
10136 << QgsExpressionFunction::Parameter( u"y_scale"_s )
10137 << QgsExpressionFunction::Parameter( u"center"_s, true ),
10138 fcnScale,
10139 u"GeometryGroup"_s
10140 )
10141 << new QgsStaticExpressionFunction(
10142 u"affine_transform"_s,
10144 << QgsExpressionFunction::Parameter( u"geometry"_s )
10145 << QgsExpressionFunction::Parameter( u"delta_x"_s )
10146 << QgsExpressionFunction::Parameter( u"delta_y"_s )
10147 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
10148 << QgsExpressionFunction::Parameter( u"scale_x"_s )
10149 << QgsExpressionFunction::Parameter( u"scale_y"_s )
10150 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
10151 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
10152 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
10153 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
10154 fcnAffineTransform,
10155 u"GeometryGroup"_s
10156 )
10157 << new QgsStaticExpressionFunction(
10158 u"buffer"_s,
10160 << QgsExpressionFunction::Parameter( u"geometry"_s )
10161 << QgsExpressionFunction::Parameter( u"distance"_s )
10162 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
10163 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
10164 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
10165 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
10166 fcnBuffer,
10167 u"GeometryGroup"_s
10168 )
10169 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForceRHR, u"GeometryGroup"_s )
10170 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCW, u"GeometryGroup"_s )
10171 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCCW, u"GeometryGroup"_s )
10172 << new QgsStaticExpressionFunction(
10173 u"wedge_buffer"_s,
10175 << QgsExpressionFunction::Parameter( u"center"_s )
10176 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10177 << QgsExpressionFunction::Parameter( u"width"_s )
10178 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
10179 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ),
10180 fcnWedgeBuffer,
10181 u"GeometryGroup"_s
10182 )
10183 << new QgsStaticExpressionFunction(
10184 u"tapered_buffer"_s,
10186 << QgsExpressionFunction::Parameter( u"geometry"_s )
10187 << QgsExpressionFunction::Parameter( u"start_width"_s )
10188 << QgsExpressionFunction::Parameter( u"end_width"_s )
10189 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ),
10190 fcnTaperedBuffer,
10191 u"GeometryGroup"_s
10192 )
10193 << 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 )
10194 << new QgsStaticExpressionFunction(
10195 u"offset_curve"_s,
10197 << QgsExpressionFunction::Parameter( u"geometry"_s )
10198 << QgsExpressionFunction::Parameter( u"distance"_s )
10199 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10200 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10201 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10202 fcnOffsetCurve,
10203 u"GeometryGroup"_s
10204 )
10205 << new QgsStaticExpressionFunction(
10206 u"single_sided_buffer"_s,
10208 << QgsExpressionFunction::Parameter( u"geometry"_s )
10209 << QgsExpressionFunction::Parameter( u"distance"_s )
10210 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10211 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10212 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10213 fcnSingleSidedBuffer,
10214 u"GeometryGroup"_s
10215 )
10216 << new QgsStaticExpressionFunction(
10217 u"extend"_s,
10219 << QgsExpressionFunction::Parameter( u"geometry"_s )
10220 << QgsExpressionFunction::Parameter( u"start_distance"_s )
10221 << QgsExpressionFunction::Parameter( u"end_distance"_s )
10222 << QgsExpressionFunction::Parameter( u"start_deflection"_s, true, 0 )
10223 << QgsExpressionFunction::Parameter( u"end_deflection"_s, true, 0 ),
10224 fcnExtend,
10225 u"GeometryGroup"_s
10226 )
10227 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
10228 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
10229 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
10230 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
10231 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
10232 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnInteriorRingN, u"GeometryGroup"_s )
10233 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnGeometryN, u"GeometryGroup"_s )
10234 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
10235 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
10236 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry1"_s ), QgsExpressionFunction::Parameter( u"geometry2"_s ) }, fcnSharedPaths, u"GeometryGroup"_s )
10237 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
10238 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
10239 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
10240 << new QgsStaticExpressionFunction(
10241 u"smooth"_s,
10243 << QgsExpressionFunction::Parameter( u"geometry"_s )
10244 << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
10245 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
10246 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
10247 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ),
10248 fcnSmooth,
10249 u"GeometryGroup"_s
10250 )
10251 << new QgsStaticExpressionFunction(
10252 u"triangular_wave"_s,
10253 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10254 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10255 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10256 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10257 fcnTriangularWave,
10258 u"GeometryGroup"_s
10259 )
10260 << new QgsStaticExpressionFunction(
10261 u"triangular_wave_randomized"_s,
10262 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10263 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10264 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10265 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10266 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10267 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10268 fcnTriangularWaveRandomized,
10269 u"GeometryGroup"_s
10270 )
10271 << new QgsStaticExpressionFunction(
10272 u"square_wave"_s,
10273 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10274 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10275 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10276 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10277 fcnSquareWave,
10278 u"GeometryGroup"_s
10279 )
10280 << new QgsStaticExpressionFunction(
10281 u"square_wave_randomized"_s,
10282 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10283 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10284 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10285 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10286 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10287 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10288 fcnSquareWaveRandomized,
10289 u"GeometryGroup"_s
10290 )
10291 << new QgsStaticExpressionFunction(
10292 u"wave"_s,
10293 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10294 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10295 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10296 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10297 fcnRoundWave,
10298 u"GeometryGroup"_s
10299 )
10300 << new QgsStaticExpressionFunction(
10301 u"wave_randomized"_s,
10302 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10303 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10304 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10305 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10306 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10307 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10308 fcnRoundWaveRandomized,
10309 u"GeometryGroup"_s
10310 )
10311 << new QgsStaticExpressionFunction(
10312 u"apply_dash_pattern"_s,
10313 {
10314 QgsExpressionFunction::Parameter( u"geometry"_s ),
10315 QgsExpressionFunction::Parameter( u"pattern"_s ),
10316 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
10317 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
10318 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
10319 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
10320 },
10321 fcnApplyDashPattern,
10322 u"GeometryGroup"_s
10323 )
10324 << new QgsStaticExpressionFunction( u"densify_by_count"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"vertices"_s ) }, fcnDensifyByCount, u"GeometryGroup"_s )
10325 << new QgsStaticExpressionFunction( u"densify_by_distance"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"distance"_s ) }, fcnDensifyByDistance, u"GeometryGroup"_s )
10326 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
10327 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
10328 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
10329 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
10330 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
10331 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
10332 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
10333 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
10334 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
10335 << new QgsStaticExpressionFunction(
10336 u"is_empty_or_null"_s,
10337 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
10338 fcnIsEmptyOrNull,
10339 u"GeometryGroup"_s,
10340 QString(),
10341 false,
10342 QSet<QString>(),
10343 false,
10344 QStringList(),
10345 true
10346 )
10347 << 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 )
10348#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
10349 << new QgsStaticExpressionFunction(
10350 u"concave_hull"_s,
10352 << QgsExpressionFunction::Parameter( u"geometry"_s )
10353 << QgsExpressionFunction::Parameter( u"target_percent"_s )
10354 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ),
10355 fcnConcaveHull,
10356 u"GeometryGroup"_s
10357 )
10358#endif
10359 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnOrientedBBox, u"GeometryGroup"_s )
10360 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMainAngle, u"GeometryGroup"_s )
10361 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ), fcnMinimalCircle, u"GeometryGroup"_s )
10362 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDifference, u"GeometryGroup"_s )
10363 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDistance, u"GeometryGroup"_s )
10364 << new QgsStaticExpressionFunction(
10365 u"hausdorff_distance"_s,
10367 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10368 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10369 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
10370 fcnHausdorffDistance,
10371 u"GeometryGroup"_s
10372 )
10373 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersection, u"GeometryGroup"_s )
10374 << new QgsStaticExpressionFunction(
10375 u"sym_difference"_s,
10376 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10377 fcnSymDifference,
10378 u"GeometryGroup"_s,
10379 QString(),
10380 false,
10381 QSet<QString>(),
10382 false,
10383 QStringList() << u"symDifference"_s
10384 )
10385 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10386 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10387 << new QgsStaticExpressionFunction(
10388 u"geom_to_wkt"_s,
10389 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
10390 fcnGeomToWKT,
10391 u"GeometryGroup"_s,
10392 QString(),
10393 false,
10394 QSet<QString>(),
10395 false,
10396 QStringList() << u"geomToWKT"_s
10397 )
10398 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10399 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
10400 << new QgsStaticExpressionFunction(
10401 u"transform"_s,
10402 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"source_auth_id"_s ) << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
10403 fcnTransformGeometry,
10404 u"GeometryGroup"_s
10405 )
10406 << new QgsStaticExpressionFunction(
10407 u"extrude"_s,
10408 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ),
10409 fcnExtrude,
10410 u"GeometryGroup"_s,
10411 QString()
10412 )
10413 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsMultipart, u"GeometryGroup"_s )
10414 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMax, u"GeometryGroup"_s )
10415 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMin, u"GeometryGroup"_s )
10416 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMax, u"GeometryGroup"_s )
10417 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMin, u"GeometryGroup"_s )
10418 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSinuosity, u"GeometryGroup"_s )
10419 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStraightDistance2d, u"GeometryGroup"_s );
10420
10421
10422 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction(
10423 u"order_parts"_s,
10425 << QgsExpressionFunction::Parameter( u"geometry"_s )
10426 << QgsExpressionFunction::Parameter( u"orderby"_s )
10427 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
10428 fcnOrderParts,
10429 u"GeometryGroup"_s,
10430 QString()
10431 );
10432
10433 orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10434 const QList< QgsExpressionNode *> argList = node->args()->list();
10435 for ( QgsExpressionNode *argNode : argList )
10436 {
10437 if ( !argNode->isStatic( parent, context ) )
10438 return false;
10439 }
10440
10441 if ( node->args()->count() > 1 )
10442 {
10443 QgsExpressionNode *argNode = node->args()->at( 1 );
10444
10445 QString expString = argNode->eval( parent, context ).toString();
10446
10447 QgsExpression e( expString );
10448
10449 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10450 return true;
10451 }
10452
10453 return true;
10454 } );
10455
10456 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10457 if ( node->args()->count() > 1 )
10458 {
10459 QgsExpressionNode *argNode = node->args()->at( 1 );
10460 QString expression = argNode->eval( parent, context ).toString();
10462 e.prepare( context );
10463 context->setCachedValue( expression, QVariant::fromValue( e ) );
10464 }
10465 return true;
10466 } );
10467 functions << orderPartsFunc;
10468
10469 functions
10470 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnClosestPoint, u"GeometryGroup"_s )
10471 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnShortestLine, u"GeometryGroup"_s )
10472 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
10473 << new QgsStaticExpressionFunction(
10474 u"line_interpolate_point_by_m"_s,
10476 << QgsExpressionFunction::Parameter( u"geometry"_s )
10477 << QgsExpressionFunction::Parameter( u"m"_s )
10478 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10479 fcnLineInterpolatePointByM,
10480 u"GeometryGroup"_s
10481 )
10482 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
10483 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
10484 << new QgsStaticExpressionFunction(
10485 u"line_locate_m"_s,
10487 << QgsExpressionFunction::Parameter( u"geometry"_s )
10488 << QgsExpressionFunction::Parameter( u"m"_s )
10489 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10490 fcnLineLocateM,
10491 u"GeometryGroup"_s
10492 )
10493 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
10494 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
10495 << new QgsStaticExpressionFunction(
10496 u"line_substring"_s,
10497 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10498 fcnLineSubset,
10499 u"GeometryGroup"_s
10500 );
10501
10502
10503 // **Record** functions
10504
10505 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
10506 idFunc->setIsStatic( false );
10507 functions << idFunc;
10508
10509 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
10510 currentFeatureFunc->setIsStatic( false );
10511 functions << currentFeatureFunc;
10512
10513 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction(
10514 u"uuid"_s,
10515 { QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ), QgsExpressionFunction::Parameter( u"version"_s, true, 4 ) },
10516 fcnUuid,
10517 u"Record and Attributes"_s,
10518 QString(),
10519 false,
10520 QSet<QString>(),
10521 false,
10522 QStringList() << u"$uuid"_s
10523 );
10524 uuidFunc->setIsStatic( false );
10525 functions << uuidFunc;
10526
10527 functions
10528 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
10529 << new QgsStaticExpressionFunction(
10530 u"get_feature"_s,
10531 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"value"_s, true ),
10532 fcnGetFeature,
10533 u"Record and Attributes"_s,
10534 QString(),
10535 false,
10536 QSet<QString>(),
10537 false,
10538 QStringList() << u"QgsExpressionUtils::getFeature"_s
10539 )
10540 << new QgsStaticExpressionFunction(
10541 u"get_feature_by_id"_s,
10542 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"feature_id"_s ),
10543 fcnGetFeatureById,
10544 u"Record and Attributes"_s,
10545 QString(),
10546 false,
10547 QSet<QString>(),
10548 false
10549 );
10550
10551 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction(
10552 u"attributes"_s,
10553 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
10554 fcnAttributes,
10555 u"Record and Attributes"_s,
10556 QString(),
10557 false,
10558 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10559 );
10560 attributesFunc->setIsStatic( false );
10561 functions << attributesFunc;
10562 QgsStaticExpressionFunction *representAttributesFunc
10563 = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1, fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10564 representAttributesFunc->setIsStatic( false );
10565 functions << representAttributesFunc;
10566
10567 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction(
10568 u"is_feature_valid"_s,
10570 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10571 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10572 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10573 fcnValidateFeature,
10574 u"Record and Attributes"_s,
10575 QString(),
10576 false,
10577 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10578 );
10579 validateFeature->setIsStatic( false );
10580 functions << validateFeature;
10581
10582 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction(
10583 u"is_attribute_valid"_s,
10585 << QgsExpressionFunction::Parameter( u"attribute"_s, false )
10587 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10588 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10589 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10590 fcnValidateAttribute,
10591 u"Record and Attributes"_s,
10592 QString(),
10593 false,
10594 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10595 );
10596 validateAttribute->setIsStatic( false );
10597 functions << validateAttribute;
10598
10599 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction( u"maptip"_s, -1, fcnFeatureMaptip, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10600 maptipFunc->setIsStatic( false );
10601 functions << maptipFunc;
10602
10603 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction( u"display_expression"_s, -1, fcnFeatureDisplayExpression, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10604 displayFunc->setIsStatic( false );
10605 functions << displayFunc;
10606
10607 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( u"is_selected"_s, -1, fcnIsSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10608 isSelectedFunc->setIsStatic( false );
10609 functions << isSelectedFunc;
10610
10611 functions << new QgsStaticExpressionFunction( u"num_selected"_s, -1, fcnNumSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10612
10613 functions << new QgsStaticExpressionFunction(
10614 u"sqlite_fetch_and_increment"_s,
10616 << QgsExpressionFunction::Parameter( u"database"_s )
10617 << QgsExpressionFunction::Parameter( u"table"_s )
10618 << QgsExpressionFunction::Parameter( u"id_field"_s )
10619 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
10620 << QgsExpressionFunction::Parameter( u"filter_value"_s )
10621 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
10622 fcnSqliteFetchAndIncrement,
10623 u"Record and Attributes"_s
10624 );
10625
10626 // **CRS** functions
10627 functions
10628 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
10629 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
10630
10631
10632 // **Fields and Values** functions
10633 QgsStaticExpressionFunction *representValueFunc
10634 = 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 );
10635
10636 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10637 Q_UNUSED( context )
10638 if ( node->args()->count() == 1 )
10639 {
10640 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
10641 if ( colRef )
10642 {
10643 return true;
10644 }
10645 else
10646 {
10647 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
10648 return false;
10649 }
10650 }
10651 else if ( node->args()->count() == 2 )
10652 {
10653 return true;
10654 }
10655 else
10656 {
10657 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
10658 return false;
10659 }
10660 } );
10661
10662 functions << representValueFunc;
10663
10664 // **General** functions
10665 functions
10666 << new QgsStaticExpressionFunction(
10667 u"layer_property"_s,
10669 << QgsExpressionFunction::Parameter( u"layer"_s )
10670 << QgsExpressionFunction::Parameter( u"property"_s )
10671 << QgsExpressionFunction::Parameter( u"translate"_s, true, true ),
10672 fcnGetLayerProperty,
10673 u"Map Layers"_s
10674 )
10675 << new QgsStaticExpressionFunction( u"decode_uri"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"part"_s, true ), fcnDecodeUri, u"Map Layers"_s )
10676 << new QgsStaticExpressionFunction( u"mime_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary_data"_s ), fcnMimeType, u"General"_s )
10677 << new QgsStaticExpressionFunction(
10678 u"raster_statistic"_s,
10679 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"statistic"_s ),
10680 fcnGetRasterBandStat,
10681 u"Rasters"_s
10682 );
10683
10684 // **var** function
10685 QgsStaticExpressionFunction *varFunction
10686 = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
10687 varFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10688 /* A variable node is static if it has a static name and the name can be found at prepare
10689 * time and is tagged with isStatic.
10690 * It is not static if a variable is set during iteration or not tagged isStatic.
10691 * (e.g. geom_part variable)
10692 */
10693 if ( node->args()->count() > 0 )
10694 {
10695 QgsExpressionNode *argNode = node->args()->at( 0 );
10696
10697 if ( !argNode->isStatic( parent, context ) )
10698 return false;
10699
10700 const QString varName = argNode->eval( parent, context ).toString();
10701 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
10702 return false;
10703
10704 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
10705 return scope ? scope->isStatic( varName ) : false;
10706 }
10707 return false;
10708 } );
10709 varFunction->setUsesGeometryFunction( []( const QgsExpressionNodeFunction *node ) -> bool {
10710 if ( node && node->args()->count() > 0 )
10711 {
10712 QgsExpressionNode *argNode = node->args()->at( 0 );
10713 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
10714 {
10715 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
10716 return true;
10717 }
10718 }
10719 return false;
10720 } );
10721
10722 functions << varFunction;
10723
10724 QgsStaticExpressionFunction *evalTemplateFunction
10725 = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10726 evalTemplateFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10727 if ( node->args()->count() > 0 )
10728 {
10729 QgsExpressionNode *argNode = node->args()->at( 0 );
10730
10731 if ( argNode->isStatic( parent, context ) )
10732 {
10733 QString expString = argNode->eval( parent, context ).toString();
10734
10735 QgsExpression e( expString );
10736
10737 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10738 return true;
10739 }
10740 }
10741
10742 return false;
10743 } );
10744 functions << evalTemplateFunction;
10745
10746 QgsStaticExpressionFunction *evalFunc
10747 = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10748 evalFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10749 if ( node->args()->count() > 0 )
10750 {
10751 QgsExpressionNode *argNode = node->args()->at( 0 );
10752
10753 if ( argNode->isStatic( parent, context ) )
10754 {
10755 QString expString = argNode->eval( parent, context ).toString();
10756
10757 QgsExpression e( expString );
10758
10759 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10760 return true;
10761 }
10762 }
10763
10764 return false;
10765 } );
10766
10767 functions << evalFunc;
10768
10769 QgsStaticExpressionFunction *attributeFunc
10770 = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10771 attributeFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10772 const QList< QgsExpressionNode *> argList = node->args()->list();
10773 for ( QgsExpressionNode *argNode : argList )
10774 {
10775 if ( !argNode->isStatic( parent, context ) )
10776 return false;
10777 }
10778
10779 if ( node->args()->count() == 1 )
10780 {
10781 // not static -- this is the variant which uses the current feature taken direct from the expression context
10782 return false;
10783 }
10784
10785 return true;
10786 } );
10787 functions << attributeFunc;
10788
10789 functions
10790 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10791 << new QgsWithVariableExpressionFunction()
10792 << new QgsStaticExpressionFunction(
10793 u"raster_value"_s,
10794 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10795 fcnRasterValue,
10796 u"Rasters"_s
10797 )
10798 << new QgsStaticExpressionFunction(
10799 u"raster_attributes"_s,
10800 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10801 fcnRasterAttributes,
10802 u"Rasters"_s
10803 )
10804
10805 // functions for arrays
10806 << new QgsArrayForeachExpressionFunction()
10807 << new QgsArrayFilterExpressionFunction()
10808 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10809 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10810 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10811 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10812 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10813 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayAll, u"Arrays"_s )
10814 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10815 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10816 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10817 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10818 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10819 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10820 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10821 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10822 << 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 )
10823 << 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 )
10824 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10825 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10826 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10827 << new QgsStaticExpressionFunction(
10828 u"array_insert"_s,
10829 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10830 fcnArrayInsert,
10831 u"Arrays"_s
10832 )
10833 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10834 << new QgsStaticExpressionFunction(
10835 u"array_remove_all"_s,
10836 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10837 fcnArrayRemoveAll,
10838 u"Arrays"_s,
10839 QString(),
10840 false,
10841 QSet<QString>(),
10842 false,
10843 QStringList(),
10844 true
10845 )
10846 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10847 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"priority"_s ), fcnArrayPrioritize, u"Arrays"_s )
10848 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10849 << new QgsStaticExpressionFunction(
10850 u"array_slice"_s,
10851 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ),
10852 fcnArraySlice,
10853 u"Arrays"_s
10854 )
10855 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10856 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10857 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10858 << new QgsStaticExpressionFunction(
10859 u"array_to_string"_s,
10861 << QgsExpressionFunction::Parameter( u"array"_s )
10862 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10863 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10864 fcnArrayToString,
10865 u"Arrays"_s
10866 )
10867 << new QgsStaticExpressionFunction(
10868 u"string_to_array"_s,
10870 << QgsExpressionFunction::Parameter( u"string"_s )
10871 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10872 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10873 fcnStringToArray,
10874 u"Arrays"_s
10875 )
10876 << new QgsStaticExpressionFunction(
10877 u"generate_series"_s,
10878 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ),
10879 fcnGenerateSeries,
10880 u"Arrays"_s
10881 )
10882 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10883
10884 //functions for maps
10885 << 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 )
10886 << 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 )
10887 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10888 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10889 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10890 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10891 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10892 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10893 << 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 )
10894 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10895 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10896 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10897 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"prefix"_s ), fcnMapPrefixKeys, u"Maps"_s )
10898 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlTable, u"Maps"_s )
10899 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlDefinitionList, u"Maps"_s )
10900 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnToFormUrlEncode, u"Maps"_s )
10901
10902 ;
10903
10905
10906 //QgsExpression has ownership of all built-in functions
10907 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10908 {
10909 *sOwnedFunctions() << func;
10910 *sBuiltinFunctions() << func->name();
10911 sBuiltinFunctions()->append( func->aliases() );
10912 }
10913 }
10914 return functions;
10915}
10916
10917bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10918{
10919 int fnIdx = functionIndex( function->name() );
10920 if ( fnIdx != -1 )
10921 {
10922 return false;
10923 }
10924
10925 QMutexLocker locker( &sFunctionsMutex );
10926 sFunctions()->append( function );
10927 if ( transferOwnership )
10928 sOwnedFunctions()->append( function );
10929
10930 return true;
10931}
10932
10933bool QgsExpression::unregisterFunction( const QString &name )
10934{
10935 // You can never override the built in functions.
10936 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10937 {
10938 return false;
10939 }
10940 int fnIdx = functionIndex( name );
10941 if ( fnIdx != -1 )
10942 {
10943 QMutexLocker locker( &sFunctionsMutex );
10944 sFunctions()->removeAt( fnIdx );
10945 sFunctionIndexMap.clear();
10946 return true;
10947 }
10948 return false;
10949}
10950
10952{
10953 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10954 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10955 {
10956 sBuiltinFunctions()->removeAll( func->name() );
10957 for ( const QString &alias : func->aliases() )
10959 sBuiltinFunctions()->removeAll( alias );
10960 }
10961
10962 sFunctions()->removeAll( func );
10963 }
10964
10965 qDeleteAll( *sOwnedFunctions() );
10966 sOwnedFunctions()->clear();
10968
10969const QStringList &QgsExpression::BuiltinFunctions()
10970{
10971 if ( sBuiltinFunctions()->isEmpty() )
10972 {
10973 Functions(); // this method builds the gmBuiltinFunctions as well
10974 }
10975 return *sBuiltinFunctions();
10976}
10977
10980 u"array_foreach"_s, // skip-keyword-check
10981 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ),
10982 u"Arrays"_s
10983 )
10984{}
10985
10987{
10988 bool isStatic = false;
10989
10990 QgsExpressionNode::NodeList *args = node->args();
10992 if ( args->count() < 2 )
10993 return false;
10994
10995 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10996 {
10997 isStatic = true;
10998 }
10999 return isStatic;
11000}
11001
11003{
11004 Q_UNUSED( node )
11005 QVariantList result;
11006
11007 if ( args->count() < 2 )
11008 // error
11009 return result;
11010
11011 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11012
11013 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11014 std::unique_ptr< QgsExpressionContext > tempContext;
11015 if ( !subContext )
11016 {
11017 tempContext = std::make_unique< QgsExpressionContext >();
11018 subContext = tempContext.get();
11019 }
11020
11021 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11022 subContext->appendScope( subScope );
11023
11024 int i = 0;
11025 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
11026 {
11027 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
11028 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
11029 result << args->at( 1 )->eval( parent, subContext );
11030 }
11031
11032 if ( context )
11033 delete subContext->popScope();
11034
11035 return result;
11036}
11037
11038QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11040 // This is a dummy function, all the real handling is in run
11041 Q_UNUSED( values )
11042 Q_UNUSED( context )
11043 Q_UNUSED( parent )
11044 Q_UNUSED( node )
11045
11046 Q_ASSERT( false );
11047 return QVariant();
11048}
11049
11051{
11052 QgsExpressionNode::NodeList *args = node->args();
11053
11054 if ( args->count() < 2 )
11055 // error
11056 return false;
11057
11058 args->at( 0 )->prepare( parent, context );
11059
11060 QgsExpressionContext subContext;
11061 if ( context )
11062 subContext = *context;
11065 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11066 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
11067 subContext.appendScope( subScope );
11068
11069 args->at( 1 )->prepare( parent, &subContext );
11070
11071 return true;
11072}
11073
11075 : 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 )
11076{}
11077
11079{
11080 bool isStatic = false;
11081
11082 QgsExpressionNode::NodeList *args = node->args();
11084 if ( args->count() < 2 )
11085 return false;
11086
11087 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11088 {
11089 isStatic = true;
11090 }
11091 return isStatic;
11092}
11093
11095{
11096 Q_UNUSED( node )
11097 QVariantList result;
11098
11099 if ( args->count() < 2 )
11100 // error
11101 return result;
11102
11103 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11104
11105 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11106 std::unique_ptr< QgsExpressionContext > tempContext;
11107 if ( !subContext )
11108 {
11109 tempContext = std::make_unique< QgsExpressionContext >();
11110 subContext = tempContext.get();
11111 }
11112
11113 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11114 subContext->appendScope( subScope );
11115
11116 int limit = 0;
11117 if ( args->count() >= 3 )
11118 {
11119 const QVariant limitVar = args->at( 2 )->eval( parent, context );
11120
11121 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
11122 {
11123 limit = limitVar.toInt();
11124 }
11125 else
11126 {
11127 return result;
11128 }
11129 }
11130
11131 for ( const QVariant &value : array )
11132 {
11133 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
11134 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
11135 {
11136 result << value;
11137
11138 if ( limit > 0 && limit == result.size() )
11139 break;
11140 }
11141 }
11142
11143 if ( context )
11144 delete subContext->popScope();
11145
11146 return result;
11147}
11148
11149QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11151 // This is a dummy function, all the real handling is in run
11152 Q_UNUSED( values )
11153 Q_UNUSED( context )
11154 Q_UNUSED( parent )
11155 Q_UNUSED( node )
11156
11157 Q_ASSERT( false );
11158 return QVariant();
11159}
11160
11162{
11163 QgsExpressionNode::NodeList *args = node->args();
11164
11165 if ( args->count() < 2 )
11166 // error
11167 return false;
11168
11169 args->at( 0 )->prepare( parent, context );
11170
11171 QgsExpressionContext subContext;
11172 if ( context )
11173 subContext = *context;
11174
11176 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11177 subContext.appendScope( subScope );
11178
11179 args->at( 1 )->prepare( parent, &subContext );
11180
11181 return true;
11182}
11184 : 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 )
11185{}
11186
11188{
11189 bool isStatic = false;
11190
11191 QgsExpressionNode::NodeList *args = node->args();
11192
11193 if ( args->count() < 3 )
11194 return false;
11195
11196 // We only need to check if the node evaluation is static, if both - name and value - are static.
11197 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11198 {
11199 QVariant name = args->at( 0 )->eval( parent, context );
11200 QVariant value = args->at( 1 )->eval( parent, context );
11202 // Temporarily append a new scope to provide the variable
11203 appendTemporaryVariable( context, name.toString(), value );
11204 if ( args->at( 2 )->isStatic( parent, context ) )
11205 isStatic = true;
11206 popTemporaryVariable( context );
11207 }
11208
11209 return isStatic;
11210}
11211
11213{
11214 Q_UNUSED( node )
11215 QVariant result;
11216
11217 if ( args->count() < 3 )
11218 // error
11219 return result;
11220
11221 QVariant name = args->at( 0 )->eval( parent, context );
11222 QVariant value = args->at( 1 )->eval( parent, context );
11223
11224 const QgsExpressionContext *updatedContext = context;
11225 std::unique_ptr< QgsExpressionContext > tempContext;
11226 if ( !updatedContext )
11227 {
11228 tempContext = std::make_unique< QgsExpressionContext >();
11229 updatedContext = tempContext.get();
11231
11232 appendTemporaryVariable( updatedContext, name.toString(), value );
11233 result = args->at( 2 )->eval( parent, updatedContext );
11234
11235 if ( context )
11236 popTemporaryVariable( updatedContext );
11237
11238 return result;
11239}
11240
11241QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11243 // This is a dummy function, all the real handling is in run
11244 Q_UNUSED( values )
11245 Q_UNUSED( context )
11246 Q_UNUSED( parent )
11247 Q_UNUSED( node )
11248
11249 Q_ASSERT( false );
11250 return QVariant();
11251}
11252
11254{
11255 QgsExpressionNode::NodeList *args = node->args();
11256
11257 if ( args->count() < 3 )
11258 // error
11259 return false;
11260
11261 QVariant name = args->at( 0 )->prepare( parent, context );
11262 QVariant value = args->at( 1 )->prepare( parent, context );
11263
11264 const QgsExpressionContext *updatedContext = context;
11265 std::unique_ptr< QgsExpressionContext > tempContext;
11266 if ( !updatedContext )
11267 {
11268 tempContext = std::make_unique< QgsExpressionContext >();
11269 updatedContext = tempContext.get();
11270 }
11271
11272 appendTemporaryVariable( updatedContext, name.toString(), value );
11273 args->at( 2 )->prepare( parent, updatedContext );
11274
11275 if ( context )
11276 popTemporaryVariable( updatedContext );
11277
11278 return true;
11279}
11280
11281void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
11282{
11283 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11284 delete updatedContext->popScope();
11285}
11286
11287void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
11288{
11289 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
11290 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
11291
11292 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11293 updatedContext->appendScope( scope );
11294}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2298
@ GEOS
Use GEOS implementation.
Definition qgis.h:2300
@ QGIS
Use internal implementation.
Definition qgis.h:2299
@ Left
Buffer to left of line.
Definition qgis.h:2248
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3502
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3504
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3503
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3505
@ Success
Operation succeeded.
Definition qgis.h:2193
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3214
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2359
@ 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:2272
@ Bevel
Use beveled joins.
Definition qgis.h:2275
@ Round
Use rounded joins.
Definition qgis.h:2273
@ Miter
Use mitered joins.
Definition qgis.h:2274
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6607
@ StdDev
Standard deviation.
Definition qgis.h:6614
@ NoStatistic
No statistic.
Definition qgis.h:6608
@ 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:2259
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2261
@ Round
Round cap.
Definition qgis.h:2260
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2262
Aggregate
Available aggregates to calculate.
Definition qgis.h:6484
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6501
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6498
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6491
@ Median
Median of values (numeric fields only).
Definition qgis.h:6492
@ Max
Max of values.
Definition qgis.h:6489
@ Min
Min of values.
Definition qgis.h:6488
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6502
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6495
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6506
@ Sum
Sum of values.
Definition qgis.h:6490
@ Minority
Minority of values.
Definition qgis.h:6496
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6487
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6505
@ Majority
Majority of values.
Definition qgis.h:6497
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6494
@ Count
Count.
Definition qgis.h:6485
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6499
@ CountDistinct
Number of distinct values.
Definition qgis.h:6486
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6503
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6504
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6500
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3487
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3490
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3492
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3491
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3489
@ NoRule
No special rule.
Definition qgis.h:3488
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2343
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2344
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2345
@ Point
Point.
Definition qgis.h:296
@ PointM
PointM.
Definition qgis.h:329
@ PointZ
PointZ.
Definition qgis.h:313
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ PointZM
PointZM.
Definition qgis.h:345
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:46
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:282
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:277
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:41
Defines a QGIS exception class.
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
An abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
QgsExpressionFunction(const QString &fnname, int params, const QString &group, const QString &helpText=QString(), bool lazyEval=false, bool handlesNull=false, bool isContextual=false)
Constructor for function which uses unnamed parameters.
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
const QString helpText() const
The help text for the function.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes its value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions).
friend class QgsExpressionNodeFunction
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsExpression(const QString &expr)
Creates a new expression based on the provided string.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:65
QgsFeatureId id
Definition qgsfeature.h:63
QgsGeometry geometry
Definition qgsfeature.h:66
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:754
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Encapsulates parameters under which a geometry operation is performed.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points shared by this geometry and other.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false, QgsFeedback *feedback=nullptr) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
Q_INVOKABLE bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
bool isExactlyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
Q_INVOKABLE bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry extendLine(double startDistance, double endDistance, double startDeflection=0, double endDeflection=0) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
Q_INVOKABLE bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
Q_INVOKABLE bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QgsFeedback *feedback=nullptr) const
Attempts to make an invalid geometry valid without losing vertices.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments, QgsFeedback *feedback=nullptr) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
bool isFuzzyEqual(const QgsGeometry &geometry, double epsilon=1e-4, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry within the tolerance epsilon using the specified backend.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
bool isTopologicallyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::GEOS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometry simplify(double tolerance, QgsFeedback *feedback=nullptr) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Q_INVOKABLE bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
Q_INVOKABLE bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:175
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2959
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
A representation of the interval between two datetime values.
Definition qgsinterval.h:52
bool isValid() const
Returns true if the interval is valid.
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
double years() const
Returns the interval duration in years (based on an average year length).
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Represents a model of the Earth's magnetic field.
static bool fieldComponentsWithTimeDerivatives(double Bx, double By, double Bz, double Bxt, double Byt, double Bzt, double &H, double &F, double &D, double &I, double &Ht, double &Ft, double &Dt, double &It)
Compute various quantities dependent on a magnetic field and their rates of change.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
virtual Q_INVOKABLE QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:86
QgsLayerMetadata metadata
Definition qgsmaplayer.h:89
Qgis::LayerType type
Definition qgsmaplayer.h:93
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString mapTipTemplate
Definition qgsmaplayer.h:96
Implementation of a geometry simplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:245
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:737
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:603
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:460
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:138
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
double m
Definition qgspoint.h:59
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:749
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)
Decodes a string to a color.
static QString encodeColor(const QColor &color)
Lossy-encodes a color to a string.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:596
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:7672
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8015
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8014
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7511
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7417
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
const QString cacheKey(const QString &pathIn)
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
std::function< bool(const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend)> RelationFunction
allows geometry function with different parameters to be used with the same executeGeomOverlay functi...
double qDateTimeToDecimalYear(const QDateTime &dateTime)
QList< QgsExpressionFunction * > ExpressionFunctionList
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColorObject(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
#define SET_EVAL_ERROR(x)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:98
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:63
const QgsMapLayer * layer
Definition qgsogcutils.h:71
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:72
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:35