QGIS API Documentation 4.3.0-Master (0cfde48c85b)
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
956 // get project from layer
957 QgsProject *project = vl->project();
958 if ( !project )
959 {
960 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
961 return QVariant();
962 }
963
964 // check relation exists
965 QgsRelation relation = project->relationManager()->relation( relationId );
966
967 if ( !relation.isValid() || relation.referencedLayer() != vl )
968 {
969 // check for relations by name
970 QList< QgsRelation > relations = project->relationManager()->relationsByName( relationId );
971 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
972 {
973 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
974 return QVariant();
975 }
976 else
977 {
978 relation = relations.at( 0 );
979 }
980 }
981
982 QgsVectorLayer *childLayer = relation.referencingLayer();
983
984 // second node is aggregate type
985 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
987 value = node->eval( parent, context );
989 bool ok = false;
990 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
991 if ( !ok )
992 {
993 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
994 return QVariant();
995 }
996
997 //third node is subexpression (or field name)
998 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
1000 QString subExpression = node->dump();
1001
1002 //optional fourth node is concatenator
1004 if ( values.count() > 3 )
1005 {
1006 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1008 value = node->eval( parent, context );
1010 parameters.delimiter = value.toString();
1011 }
1012
1013 //optional fifth node is order by
1014 QString orderBy;
1015 if ( values.count() > 4 )
1016 {
1017 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
1019 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1020 if ( !nl || nl->value().isValid() )
1021 {
1022 orderBy = node->dump();
1023 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1024 }
1025 }
1026
1027 if ( !context->hasFeature() )
1028 return QVariant();
1029 QgsFeature f = context->feature();
1030
1031 parameters.filter = relation.getRelatedFeaturesFilter( f );
1032
1033 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 );
1034 if ( context->hasCachedValue( cacheKey ) )
1035 return context->cachedValue( cacheKey );
1036
1037 QVariant result;
1038 ok = false;
1039
1040
1041 QgsExpressionContext subContext( *context );
1042 QString error;
1043 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1044
1045 if ( !ok )
1046 {
1047 if ( !error.isEmpty() )
1048 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1049 else
1050 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1051 return QVariant();
1052 }
1053
1054 // cache value
1055 context->setCachedValue( cacheKey, result );
1056 return result;
1057}
1058
1059
1060static QVariant fcnAggregateGeneric(
1061 Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1
1062)
1063{
1064 if ( !context )
1065 {
1066 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1067 return QVariant();
1068 }
1069
1070 // first step - find current layer
1071
1072 // TODO this expression function is NOT thread safe
1074 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
1076 if ( !vl )
1077 {
1078 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1079 return QVariant();
1080 }
1081
1082 //lazy eval, so we need to evaluate nodes now
1083
1084 //first node is subexpression (or field name)
1085 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1087 QString subExpression = node->dump();
1088
1089 //optional second node is group by
1090 QString groupBy;
1091 if ( values.count() > 1 )
1092 {
1093 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
1095 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1096 if ( !nl || nl->value().isValid() )
1097 groupBy = node->dump();
1098 }
1099
1100 //optional third node is filter
1101 if ( values.count() > 2 )
1102 {
1103 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
1105 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1106 if ( !nl || nl->value().isValid() )
1107 parameters.filter = node->dump();
1108 }
1109
1110 //optional order by node, if supported
1111 QString orderBy;
1112 if ( orderByPos >= 0 && values.count() > orderByPos )
1113 {
1114 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
1116 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1117 if ( !nl || nl->value().isValid() )
1118 {
1119 orderBy = node->dump();
1120 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1121 }
1122 }
1123
1124 // build up filter with group by
1125
1126 // find current group by value
1127 if ( !groupBy.isEmpty() )
1128 {
1129 QgsExpression groupByExp( groupBy );
1130 QVariant groupByValue = groupByExp.evaluate( context );
1131 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy, QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s, QgsExpression::quotedValue( groupByValue ) );
1132 if ( !parameters.filter.isEmpty() )
1133 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1134 else
1135 parameters.filter = groupByClause;
1136 }
1137
1138 QgsExpression subExp( subExpression );
1139 QgsExpression filterExp( parameters.filter );
1140
1141 bool isStatic = true;
1142 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1143 for ( const QString &varName : refVars )
1144 {
1145 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1146 if ( scope && !scope->isStatic( varName ) )
1147 {
1148 isStatic = false;
1149 break;
1150 }
1151 }
1152
1153 QString cacheKey;
1154 if ( !isStatic )
1155 {
1156 bool ok = false;
1157 const QString contextHash = context->uniqueHash( ok, refVars );
1158 if ( ok )
1159 {
1160 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
1161 }
1162 }
1163 else
1164 {
1165 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1166 }
1167
1168 if ( context->hasCachedValue( cacheKey ) )
1169 return context->cachedValue( cacheKey );
1170
1171 QVariant result;
1172 bool ok = false;
1173
1174 QgsExpressionContext subContext( *context );
1176 subScope->setVariable( u"parent"_s, context->feature(), true );
1177 subContext.appendScope( subScope );
1178 QString error;
1179 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1180
1181 if ( !ok )
1182 {
1183 if ( !error.isEmpty() )
1184 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1185 else
1186 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1187 return QVariant();
1188 }
1189
1190 // cache value
1191 context->setCachedValue( cacheKey, result );
1192 return result;
1193}
1194
1195
1196static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1197{
1198 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1199}
1200
1201static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1202{
1203 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1204}
1205
1206static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1207{
1208 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1209}
1210
1211static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1212{
1213 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1214}
1215
1216static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1217{
1218 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1219}
1220
1221static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1222{
1223 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1224}
1225
1226static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1227{
1228 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1229}
1230
1231static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1232{
1233 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1234}
1235
1236static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1237{
1238 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1239}
1240
1241static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1244}
1245
1246static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1247{
1248 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1249}
1250
1251static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1252{
1253 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1254}
1255
1256static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1257{
1258 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1259}
1260
1261static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1262{
1263 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1264}
1265
1266static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1267{
1268 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1269}
1270
1271static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1272{
1273 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1274}
1275
1276static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1277{
1278 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1279}
1280
1281static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1282{
1283 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1284}
1285
1286static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1287{
1289
1290 //fourth node is concatenator
1291 if ( values.count() > 3 )
1292 {
1293 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1295 QVariant value = node->eval( parent, context );
1297 parameters.delimiter = value.toString();
1298 }
1299
1300 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1301}
1302
1303static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1304{
1306
1307 //fourth node is concatenator
1308 if ( values.count() > 3 )
1309 {
1310 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1312 QVariant value = node->eval( parent, context );
1314 parameters.delimiter = value.toString();
1315 }
1316
1317 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1318}
1319
1320static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1321{
1322 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1323}
1324
1325static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1326{
1327 if ( !context )
1328 return QVariant();
1329
1330 QVariant scale = context->variable( u"map_scale"_s );
1331 bool ok = false;
1332 if ( QgsVariantUtils::isNull( scale ) )
1333 return QVariant();
1334
1335 const double v = scale.toDouble( &ok );
1336 if ( ok )
1337 return v;
1338 return QVariant();
1339}
1340
1341static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1342{
1343 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1344 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1345 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1346
1347 // force testValue to sit inside the range specified by the min and max value
1348 if ( testValue <= minValue )
1349 {
1350 return QVariant( minValue );
1351 }
1352 else if ( testValue >= maxValue )
1353 {
1354 return QVariant( maxValue );
1355 }
1356 else
1357 {
1358 return QVariant( testValue );
1359 }
1360}
1361
1362static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1363{
1364 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1365 return QVariant( std::floor( x ) );
1366}
1367
1368static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1369{
1370 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1371 return QVariant( std::ceil( x ) );
1372}
1373
1374static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1375{
1376 const QVariant value = values.at( 0 );
1377 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1378 {
1379 return QVariant( false );
1380 }
1381 else if ( value.userType() == QMetaType::QString )
1382 {
1383 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1384 return QVariant( !value.toString().isEmpty() );
1385 }
1386 else if ( QgsExpressionUtils::isList( value ) )
1387 {
1388 return !value.toList().isEmpty();
1389 }
1390 return QVariant( value.toBool() );
1391}
1392static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1393{
1394 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1395}
1396static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1397{
1398 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1399}
1400static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1401{
1402 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1403}
1404
1405static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1406{
1407 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1408 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1409 if ( format.isEmpty() && !language.isEmpty() )
1410 {
1411 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1412 return QVariant( QDateTime() );
1413 }
1414
1415 if ( format.isEmpty() && language.isEmpty() )
1416 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1417
1418 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1419 QLocale locale = QLocale();
1420 if ( !language.isEmpty() )
1421 {
1422 locale = QLocale( language );
1423 }
1424
1425 QDateTime datetime = locale.toDateTime( datetimestring, format );
1426 if ( !datetime.isValid() )
1427 {
1428 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1429 datetime = QDateTime();
1430 }
1431 return QVariant( datetime );
1432}
1433
1434static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1435{
1436 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1437 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1438 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1439
1440 const QDate date( year, month, day );
1441 if ( !date.isValid() )
1442 {
1443 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1444 return QVariant();
1445 }
1446 return QVariant( date );
1447}
1448
1449static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1450{
1451 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1452 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1453 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1454
1455 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1456 if ( !time.isValid() )
1457 {
1458 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1459 return QVariant();
1460 }
1461 return QVariant( time );
1462}
1463
1464static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1465{
1466 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1467 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1468 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1469 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1470 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1471 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1472
1473 const QDate date( year, month, day );
1474 if ( !date.isValid() )
1475 {
1476 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1477 return QVariant();
1478 }
1479 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1480 if ( !time.isValid() )
1481 {
1482 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1483 return QVariant();
1484 }
1485 return QVariant( QDateTime( date, time ) );
1486}
1487
1488static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1489{
1490 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1491
1492 QTimeZone tz;
1493
1494#if QT_FEATURE_timezone > 0
1495 if ( !timeZoneId.isEmpty() )
1496 {
1497 tz = QTimeZone( timeZoneId.toUtf8() );
1498 }
1499
1500 if ( !tz.isValid() )
1501 {
1502 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1503 return QVariant();
1504 }
1505
1506#else
1507 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1508#endif
1509 return QVariant::fromValue( tz );
1510}
1511
1512static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1513{
1514#if QT_FEATURE_timezone > 0
1515 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1516 if ( datetime.isValid() )
1517 {
1518 return QVariant::fromValue( datetime.timeZone() );
1519 }
1520 return QVariant();
1521#else
1522 Q_UNUSED( values )
1523 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1524 return QVariant();
1525#endif
1526}
1527
1528static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1529{
1530#if QT_FEATURE_timezone > 0
1531 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1532 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1533 if ( datetime.isValid() && tz.isValid() )
1534 {
1535 datetime.setTimeZone( tz );
1536 return QVariant::fromValue( datetime );
1537 }
1538 return QVariant();
1539#else
1540 Q_UNUSED( values )
1541 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1542 return QVariant();
1543#endif
1544}
1545
1546static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1547{
1548#if QT_FEATURE_timezone > 0
1549 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1550 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1551 if ( datetime.isValid() && tz.isValid() )
1552 {
1553 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1554 }
1555 return QVariant();
1556#else
1557 Q_UNUSED( values )
1558 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1559 return QVariant();
1560#endif
1561}
1562
1563static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1564{
1565#if QT_FEATURE_timezone > 0
1566 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1567 if ( timeZone.isValid() )
1568 {
1569 return QString( timeZone.id() );
1570 }
1571 return QVariant();
1572#else
1573 Q_UNUSED( values )
1574 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1575 return QVariant();
1576#endif
1577}
1578
1579static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1580{
1581 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1582 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1583 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1584 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1585 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1586 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1587 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1588
1589 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1590}
1591
1592static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1593{
1594 for ( const QVariant &value : values )
1595 {
1596 if ( QgsVariantUtils::isNull( value ) )
1597 continue;
1598 return value;
1599 }
1600 return QVariant();
1601}
1602
1603static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1604{
1605 const QVariant val1 = values.at( 0 );
1606 const QVariant val2 = values.at( 1 );
1607
1608 if ( val1 == val2 )
1609 return QVariant();
1610 else
1611 return val1;
1612}
1613
1614static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1615{
1616 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1617 return QVariant( str.toLower() );
1618}
1619
1620static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1621{
1622 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1623 return QVariant( str.toUpper() );
1624}
1625
1626static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1627{
1628 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1629 QStringList elems = str.split( ' ' );
1630 for ( int i = 0; i < elems.size(); i++ )
1631 {
1632 if ( elems[i].size() > 1 )
1633 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1634 }
1635 return QVariant( elems.join( ' '_L1 ) );
1636}
1637
1638static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1639{
1640 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1641 return QVariant( str.trimmed() );
1642}
1643
1644static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1645{
1646 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1647
1648 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1649
1650 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1651 str.replace( re, QString() );
1652 return QVariant( str );
1653}
1654
1655static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1656{
1657 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1658
1659 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1660
1661 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1662 str.replace( re, QString() );
1663 return QVariant( str );
1664}
1665
1666static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1667{
1668 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1669 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1670 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1671}
1672
1673static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1674{
1675 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1676 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1677 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1678}
1679
1680static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1681{
1682 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1683 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1684 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1685 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1686}
1687
1688static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689{
1690 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1691 return QVariant( QgsStringUtils::soundex( string ) );
1692}
1693
1694static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1695{
1696 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1697 return QVariant( QString( character ) );
1698}
1699
1700static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1701{
1702 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1703
1704 if ( value.isEmpty() )
1705 {
1706 return QVariant();
1707 }
1708
1709 int res = value.at( 0 ).unicode();
1710 return QVariant( res );
1711}
1712
1713static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1714{
1715 if ( values.length() == 2 || values.length() == 3 )
1716 {
1717 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1718 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1719
1720 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1721
1722 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1723 }
1724
1725 return QVariant();
1726}
1727
1728static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1729{
1730 // two variants, one for geometry, one for string
1731
1732 //geometry variant
1733 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1734 if ( !geom.isNull() )
1735 {
1736 if ( geom.type() == Qgis::GeometryType::Line )
1737 return QVariant( geom.length() );
1738 else
1739 return QVariant();
1740 }
1741
1742 //otherwise fall back to string variant
1743 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1744 return QVariant( str.length() );
1745}
1746
1747static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1748{
1749 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1750
1751 if ( geom.type() != Qgis::GeometryType::Line )
1752 return QVariant();
1753
1754 double totalLength = 0;
1755 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1756 {
1758 {
1759 totalLength += line->length3D();
1760 }
1761 else
1762 {
1763 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1764 totalLength += segmentized->length3D();
1765 }
1766 }
1767
1768 return totalLength;
1769}
1770
1771
1772static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1773{
1774 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1775 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1776 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1777}
1778
1779static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1780{
1781 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1782 {
1783 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1784 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1785 QVector< QPair< QString, QString > > mapItems;
1786
1787 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1788 {
1789 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1790 }
1791
1792 // larger keys should be replaced first since they may contain whole smaller keys
1793 std::sort( mapItems.begin(), mapItems.end(), []( const QPair< QString, QString > &pair1, const QPair< QString, QString > &pair2 ) { return ( pair1.first.length() > pair2.first.length() ); } );
1794
1795 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1796 {
1797 str = str.replace( it->first, it->second );
1798 }
1799
1800 return QVariant( str );
1801 }
1802 else if ( values.count() == 3 )
1803 {
1804 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1805 QVariantList before;
1806 QVariantList after;
1807 bool isSingleReplacement = false;
1808
1809 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1810 {
1811 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1812 }
1813 else
1814 {
1815 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1816 }
1817
1818 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1819 {
1820 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1821 isSingleReplacement = true;
1822 }
1823 else
1824 {
1825 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1826 }
1827
1828 if ( !isSingleReplacement && before.length() != after.length() )
1829 {
1830 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1831 return QVariant();
1832 }
1833
1834 for ( int i = 0; i < before.length(); i++ )
1835 {
1836 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1837 }
1838
1839 return QVariant( str );
1840 }
1841 else
1842 {
1843 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1844 return QVariant();
1845 }
1846}
1847
1848static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1849{
1850 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1851 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1852 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1853
1854 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1855 if ( !re.isValid() )
1856 {
1857 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1858 return QVariant();
1859 }
1860 return QVariant( str.replace( re, after ) );
1861}
1862
1863static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1864{
1865 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1866 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1867
1868 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1869 if ( !re.isValid() )
1870 {
1871 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1872 return QVariant();
1873 }
1874 return QVariant( ( str.indexOf( re ) + 1 ) );
1875}
1876
1877static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1878{
1879 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1880 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1881 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1882
1883 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1884 if ( !re.isValid() )
1885 {
1886 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1887 return QVariant();
1888 }
1889
1890 QRegularExpressionMatch matches = re.match( str );
1891 if ( matches.hasMatch() )
1892 {
1893 QVariantList array;
1894 QStringList list = matches.capturedTexts();
1895
1896 // Skip the first string to only return captured groups
1897 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1898 {
1899 array += ( !( *it ).isEmpty() ) ? *it : empty;
1900 }
1901
1902 return QVariant( array );
1903 }
1904 else
1905 {
1906 return QVariant();
1907 }
1908}
1909
1910static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1911{
1912 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1913 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1914
1915 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1916 if ( !re.isValid() )
1917 {
1918 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1919 return QVariant();
1920 }
1921
1922 // extract substring
1923 QRegularExpressionMatch match = re.match( str );
1924 if ( match.hasMatch() )
1925 {
1926 // return first capture
1927 if ( match.lastCapturedIndex() > 0 )
1928 {
1929 // a capture group was present, so use that
1930 return QVariant( match.captured( 1 ) );
1931 }
1932 else
1933 {
1934 // no capture group, so using all match
1935 return QVariant( match.captured( 0 ) );
1936 }
1937 }
1938 else
1939 {
1940 return QVariant( "" );
1941 }
1942}
1943
1944static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1945{
1946 const int version = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1947 QUuid uuid;
1948 switch ( version )
1949 {
1950 case 4:
1951 uuid = QUuid::createUuid();
1952 break;
1953 case 7:
1954#if QT_VERSION >= QT_VERSION_CHECK( 6, 9, 0 )
1955 uuid = QUuid::createUuidV7();
1956#else
1957 parent->setEvalErrorString( QObject::tr( "UUid version 7 is not supported on this QGIS build" ) );
1958 return QVariant();
1959#endif
1960 break;
1961 default:
1962 parent->setEvalErrorString( QObject::tr( "UUid version %1 is not supported" ).arg( version ) );
1963 return QVariant();
1964 }
1965
1966 const QString format = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1967 if ( format.compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1968 return uuid.toString( QUuid::StringFormat::WithoutBraces );
1969 else if ( format.compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1970 return uuid.toString( QUuid::StringFormat::Id128 );
1971 return uuid.toString();
1972}
1973
1974static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1975{
1976 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1977 return QVariant();
1978
1979 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1980 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1981
1982 int len = 0;
1983 if ( values.at( 2 ).isValid() )
1984 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1985 else
1986 len = str.size();
1987
1988 if ( from < 0 )
1989 {
1990 from = str.size() + from;
1991 if ( from < 0 )
1992 {
1993 from = 0;
1994 }
1995 }
1996 else if ( from > 0 )
1997 {
1998 //account for the fact that substr() starts at 1
1999 from -= 1;
2000 }
2001
2002 if ( len < 0 )
2003 {
2004 len = str.size() + len - from;
2005 if ( len < 0 )
2006 {
2007 len = 0;
2008 }
2009 }
2010
2011 return QVariant( str.mid( from, len ) );
2012}
2013static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2014{
2015 FEAT_FROM_CONTEXT( context, f )
2016 return QVariant( f.id() );
2017}
2018
2019static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2020{
2021 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2022 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
2023 bool foundLayer = false;
2024 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2025 values.at( 0 ),
2026 context,
2027 parent,
2028 [parent, bandNb, geom]( QgsMapLayer *mapLayer ) {
2029 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
2030 if ( !layer || !layer->dataProvider() )
2031 {
2032 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2033 return QVariant();
2034 }
2035
2036 if ( bandNb < 1 || bandNb > layer->bandCount() )
2037 {
2038 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
2039 return QVariant();
2040 }
2041
2042 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
2043 {
2044 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
2045 return QVariant();
2046 }
2047
2048 QgsPointXY point = geom.asPoint();
2049 if ( geom.isMultipart() )
2050 {
2051 QgsMultiPointXY multiPoint = geom.asMultiPoint();
2052 if ( multiPoint.count() == 1 )
2053 {
2054 point = multiPoint[0];
2055 }
2056 else
2057 {
2058 // if the geometry contains more than one part, return an undefined value
2059 return QVariant();
2060 }
2061 }
2062
2063 double value = layer->dataProvider()->sample( point, bandNb );
2064 return std::isnan( value ) ? QVariant() : value;
2065 },
2066 foundLayer
2067 );
2068
2069 if ( !foundLayer )
2070 {
2071 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2072 return QVariant();
2073 }
2074 else
2075 {
2076 return res;
2077 }
2078}
2079
2080static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2081{
2082 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2083 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2084
2085 bool foundLayer = false;
2086 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2087 values.at( 0 ),
2088 context,
2089 parent,
2090 [parent, bandNb, value]( QgsMapLayer *mapLayer ) -> QVariant {
2091 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
2092 if ( !layer || !layer->dataProvider() )
2093 {
2094 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2095 return QVariant();
2096 }
2097
2098 if ( bandNb < 1 || bandNb > layer->bandCount() )
2099 {
2100 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
2101 return QVariant();
2102 }
2103
2104 if ( std::isnan( value ) )
2105 {
2106 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
2107 return QVariant();
2108 }
2109
2110 if ( !layer->dataProvider()->attributeTable( bandNb ) )
2111 {
2112 return QVariant();
2113 }
2114
2115 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
2116 if ( data.isEmpty() )
2117 {
2118 return QVariant();
2119 }
2120
2121 QVariantMap result;
2122 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
2123 for ( int idx = 0; idx < static_cast<int>( fields.count() ) && idx < static_cast<int>( data.count() ); ++idx )
2124 {
2125 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
2126 if ( field.isColor() || field.isRamp() )
2127 {
2128 continue;
2129 }
2130 result.insert( fields.at( idx ).name, data.at( idx ) );
2131 }
2132
2133 return result;
2134 },
2135 foundLayer
2136 );
2137
2138 if ( !foundLayer )
2139 {
2140 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2141 return QVariant();
2142 }
2143 else
2144 {
2145 return res;
2146 }
2147}
2148
2149static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2150{
2151 if ( !context )
2152 return QVariant();
2153
2154 return context->feature();
2155}
2156
2157static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2158{
2159 QgsFeature feature;
2160 QString attr;
2161 if ( values.size() == 1 )
2162 {
2163 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2164 feature = context->feature();
2165 }
2166 else if ( values.size() == 2 )
2167 {
2168 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2169 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2170 }
2171 else
2172 {
2173 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2174 return QVariant();
2175 }
2176
2177 return feature.attribute( attr );
2178}
2179
2180static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2181{
2182 QString table { R"html(
2183 <table>
2184 <thead>
2185 <tr><th>%1</th></tr>
2186 </thead>
2187 <tbody>
2188 <tr><td>%2</td></tr>
2189 </tbody>
2190 </table>)html" };
2191 QVariantMap dict;
2192 if ( values.size() == 1 )
2193 {
2194 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2195 }
2196 else
2197 {
2198 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2199 return QVariant();
2200 }
2201
2202 if ( dict.isEmpty() )
2203 {
2204 return QVariant();
2205 }
2206
2207 QStringList headers;
2208 QStringList cells;
2209
2210 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2211 {
2212 headers.push_back( it.key().toHtmlEscaped() );
2213 cells.push_back( it.value().toString().toHtmlEscaped() );
2214 }
2215
2216 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2217}
2218
2219static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2220{
2221 QString table { R"html(
2222 <dl>
2223 %1
2224 </dl>)html" };
2225 QVariantMap dict;
2226 if ( values.size() == 1 )
2227 {
2228 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2229 }
2230 else
2231 {
2232 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2233 return QVariant();
2234 }
2235
2236 if ( dict.isEmpty() )
2237 {
2238 return QVariant();
2239 }
2240
2241 QString rows;
2242
2243 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2244 {
2245 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2246 }
2247
2248 return table.arg( rows );
2249}
2250
2251static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2252{
2253 QVariant layer;
2254 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2255 {
2256 layer = context->variable( u"layer"_s );
2257 }
2258 else
2259 {
2260 //first node is layer id or name
2261 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2263 layer = node->eval( parent, context );
2265 }
2266
2267 QgsFeature feature;
2268 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2269 {
2270 feature = context->feature();
2271 }
2272 else
2273 {
2274 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2275 }
2276
2278 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2279 if ( strength == "hard"_L1 )
2280 {
2282 }
2283 else if ( strength == "soft"_L1 )
2284 {
2286 }
2287
2288 bool foundLayer = false;
2289 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2290 layer,
2291 context,
2292 parent,
2293 [parent, feature, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2294 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2295 if ( !layer )
2296 {
2297 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2298 return QVariant();
2299 }
2300
2301 const QgsFields fields = layer->fields();
2302 bool valid = true;
2303 for ( int i = 0; i < fields.size(); i++ )
2304 {
2305 QStringList errors;
2306 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2307 if ( !valid )
2308 {
2309 break;
2310 }
2311 }
2312
2313 return valid;
2314 },
2315 foundLayer
2316 );
2317
2318 if ( !foundLayer )
2319 {
2320 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2321 return QVariant();
2322 }
2323
2324 return res;
2325}
2326
2327static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2328{
2329 QVariant layer;
2330 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2331 {
2332 layer = context->variable( u"layer"_s );
2333 }
2334 else
2335 {
2336 //first node is layer id or name
2337 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2339 layer = node->eval( parent, context );
2341 }
2342
2343 QgsFeature feature;
2344 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2345 {
2346 feature = context->feature();
2347 }
2348 else
2349 {
2350 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2351 }
2352
2354 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2355 if ( strength == "hard"_L1 )
2356 {
2358 }
2359 else if ( strength == "soft"_L1 )
2360 {
2362 }
2363
2364 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2365
2366 bool foundLayer = false;
2367 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2368 layer,
2369 context,
2370 parent,
2371 [parent, feature, attributeName, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2372 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2373 if ( !layer )
2374 {
2375 return QVariant();
2376 }
2377
2378 const int fieldIndex = layer->fields().indexFromName( attributeName );
2379 if ( fieldIndex == -1 )
2380 {
2381 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2382 return QVariant();
2383 }
2384
2385 QStringList errors;
2386 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2387 return valid;
2388 },
2389 foundLayer
2390 );
2391
2392 if ( !foundLayer )
2393 {
2394 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2395 return QVariant();
2396 }
2397
2398 return res;
2399}
2400
2401static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2402{
2403 QgsFeature feature;
2404 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2405 {
2406 feature = context->feature();
2407 }
2408 else
2409 {
2410 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2411 }
2412
2413 const QgsFields fields = feature.fields();
2414 QVariantMap result;
2415 for ( int i = 0; i < fields.count(); ++i )
2416 {
2417 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2418 }
2419 return result;
2420}
2421
2422static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2423{
2424 QgsVectorLayer *layer = nullptr;
2425 QgsFeature feature;
2426
2427 // TODO this expression function is NOT thread safe
2429 if ( values.isEmpty() )
2430 {
2431 feature = context->feature();
2432 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2433 }
2434 else if ( values.size() == 1 )
2435 {
2436 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2437 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2438 }
2439 else if ( values.size() == 2 )
2440 {
2441 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2442 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2443 }
2444 else
2445 {
2446 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2447 return QVariant();
2448 }
2450
2451 if ( !layer )
2452 {
2453 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2454 return QVariant();
2455 }
2456
2457 if ( !feature.isValid() )
2458 {
2459 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2460 return QVariant();
2461 }
2462
2463 const QgsFields fields = feature.fields();
2464 QVariantMap result;
2465 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2466 {
2467 const QString fieldName { fields.at( fieldIndex ).name() };
2468 const QVariant attributeVal = feature.attribute( fieldIndex );
2469 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2470 if ( context && context->hasCachedValue( cacheValueKey ) )
2471 {
2472 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2473 }
2474 else
2475 {
2476 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2478 QVariant cache;
2479 if ( context )
2480 {
2481 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2482
2483 if ( !context->hasCachedValue( cacheKey ) )
2484 {
2485 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2486 context->setCachedValue( cacheKey, cache );
2487 }
2488 else
2489 {
2490 cache = context->cachedValue( cacheKey );
2491 }
2492 }
2493 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2494
2495 result.insert( fields.at( fieldIndex ).name(), value );
2496
2497 if ( context )
2498 {
2499 context->setCachedValue( cacheValueKey, value );
2500 }
2501 }
2502 }
2503 return result;
2504}
2505
2506static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2507{
2508 QgsVectorLayer *layer = nullptr;
2509 QgsFeature feature;
2510 bool evaluate = true;
2511
2512 // TODO this expression function is NOT thread safe
2514 if ( values.isEmpty() )
2515 {
2516 feature = context->feature();
2517 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2518 }
2519 else if ( values.size() == 1 )
2520 {
2521 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2522 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2523 }
2524 else if ( values.size() == 2 )
2525 {
2526 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2527 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2528 }
2529 else if ( values.size() == 3 )
2530 {
2531 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2532 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2533 evaluate = values.value( 2 ).toBool();
2534 }
2535 else
2536 {
2537 if ( isMaptip )
2538 {
2539 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2540 }
2541 else
2542 {
2543 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2544 }
2545 return QVariant();
2546 }
2547
2548 if ( !layer )
2549 {
2550 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2551 return QVariant();
2552 }
2554
2555 if ( !feature.isValid() )
2556 {
2557 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2558 return QVariant();
2559 }
2560
2561 if ( !evaluate )
2562 {
2563 if ( isMaptip )
2564 {
2565 return layer->mapTipTemplate();
2566 }
2567 else
2568 {
2569 return layer->displayExpression();
2570 }
2571 }
2572
2573 QgsExpressionContext subContext( *context );
2574 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2575 subContext.setFeature( feature );
2576
2577 if ( isMaptip )
2578 {
2579 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2580 }
2581 else
2582 {
2583 QgsExpression exp( layer->displayExpression() );
2584 exp.prepare( &subContext );
2585 return exp.evaluate( &subContext ).toString();
2586 }
2587}
2588
2589static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2590{
2591 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2592}
2593
2594static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2595{
2596 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2597}
2598
2599static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2600{
2601 QgsFeature feature;
2602 QVariant layer;
2603 if ( values.isEmpty() )
2604 {
2605 feature = context->feature();
2606 layer = context->variable( u"layer"_s );
2607 }
2608 else if ( values.size() == 1 )
2609 {
2610 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2611 layer = context->variable( u"layer"_s );
2612 }
2613 else if ( values.size() == 2 )
2614 {
2615 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2616 layer = values.at( 0 );
2617 }
2618 else
2619 {
2620 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2621 return QVariant();
2622 }
2623
2624 bool foundLayer = false;
2625 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2626 layer,
2627 context,
2628 parent,
2629 [feature]( QgsMapLayer *mapLayer ) -> QVariant {
2630 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2631 if ( !layer || !feature.isValid() )
2632 {
2633 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2634 }
2635
2636 return layer->selectedFeatureIds().contains( feature.id() );
2637 },
2638 foundLayer
2639 );
2640 if ( !foundLayer )
2641 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2642 else
2643 return res;
2644}
2645
2646static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2647{
2648 QVariant layer;
2649
2650 if ( values.isEmpty() )
2651 layer = context->variable( u"layer"_s );
2652 else if ( values.count() == 1 )
2653 layer = values.at( 0 );
2654 else
2655 {
2656 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2657 return QVariant();
2658 }
2659
2660 bool foundLayer = false;
2661 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2662 layer,
2663 context,
2664 parent,
2665 []( QgsMapLayer *mapLayer ) -> QVariant {
2666 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2667 if ( !layer )
2668 {
2669 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2670 }
2671
2672 return layer->selectedFeatureCount();
2673 },
2674 foundLayer
2675 );
2676 if ( !foundLayer )
2677 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2678 else
2679 return res;
2680}
2681
2682static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2683{
2684 static QMap<QString, qlonglong> counterCache;
2685 QVariant functionResult;
2686
2687 auto fetchAndIncrementFunc = [values, parent, &functionResult]( QgsMapLayer *mapLayer, const QString &databaseArgument ) {
2688 QString database;
2689
2690 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2691
2692 if ( layer )
2693 {
2694 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2695 database = decodedUri.value( u"path"_s ).toString();
2696 if ( database.isEmpty() )
2697 {
2698 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2699 }
2700 }
2701 else
2702 {
2703 database = databaseArgument;
2704 }
2705
2706 const QString table = values.at( 1 ).toString();
2707 const QString idColumn = values.at( 2 ).toString();
2708 const QString filterAttribute = values.at( 3 ).toString();
2709 const QVariant filterValue = values.at( 4 ).toString();
2710 const QVariantMap defaultValues = values.at( 5 ).toMap();
2711
2712 // read from database
2714 sqlite3_statement_unique_ptr sqliteStatement;
2715
2716 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2717 {
2718 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2719 functionResult = QVariant();
2720 return;
2721 }
2722
2723 QString errorMessage;
2724 QString currentValSql;
2725
2726 qlonglong nextId = 0;
2727 bool cachedMode = false;
2728 bool valueRetrieved = false;
2729
2730 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2731
2732 // Running in transaction mode, check for cached value first
2733 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2734 {
2735 cachedMode = true;
2736
2737 auto cachedCounter = counterCache.find( cacheString );
2738
2739 if ( cachedCounter != counterCache.end() )
2740 {
2741 qlonglong &cachedValue = cachedCounter.value();
2742 nextId = cachedValue;
2743 nextId += 1;
2744 cachedValue = nextId;
2745 valueRetrieved = true;
2746 }
2747 }
2748
2749 // Either not in cached mode or no cached value found, obtain from DB
2750 if ( !cachedMode || !valueRetrieved )
2751 {
2752 int result = SQLITE_ERROR;
2753
2754 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2755 if ( !filterAttribute.isNull() )
2756 {
2757 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2758 }
2759
2760 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2761
2762 if ( result == SQLITE_OK )
2763 {
2764 nextId = 0;
2765 if ( sqliteStatement.step() == SQLITE_ROW )
2766 {
2767 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2768 }
2769
2770 // If in cached mode: add value to cache and connect to transaction
2771 if ( cachedMode && result == SQLITE_OK )
2772 {
2773 counterCache.insert( cacheString, nextId );
2774
2775 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() { counterCache.remove( cacheString ); } );
2776 }
2777 valueRetrieved = true;
2778 }
2779 }
2780
2781 if ( valueRetrieved )
2782 {
2783 QString upsertSql;
2784 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2785 QStringList cols;
2786 QStringList vals;
2787 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2788 vals << QgsSqliteUtils::quotedValue( nextId );
2789
2790 if ( !filterAttribute.isNull() )
2791 {
2792 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2793 vals << QgsSqliteUtils::quotedValue( filterValue );
2794 }
2795
2796 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2797 {
2798 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2799 vals << iter.value().toString();
2800 }
2801
2802 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2803 upsertSql += " VALUES "_L1;
2804 upsertSql += '(' + vals.join( ',' ) + ')';
2805
2806 int result = SQLITE_ERROR;
2807 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2808 {
2809 QgsTransaction *transaction = layer->dataProvider()->transaction();
2810 if ( transaction->executeSql( upsertSql, errorMessage ) )
2811 {
2812 result = SQLITE_OK;
2813 }
2814 }
2815 else
2816 {
2817 result = sqliteDb.exec( upsertSql, errorMessage );
2818 }
2819 if ( result == SQLITE_OK )
2820 {
2821 functionResult = QVariant( nextId );
2822 return;
2823 }
2824 else
2825 {
2826 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2827 functionResult = QVariant();
2828 return;
2829 }
2830 }
2831
2832 functionResult = QVariant();
2833 };
2834
2835 bool foundLayer = false;
2836 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer *layer ) { fetchAndIncrementFunc( layer, QString() ); }, foundLayer );
2837 if ( !foundLayer )
2838 {
2839 const QString databasePath = values.at( 0 ).toString();
2840 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath] { fetchAndIncrementFunc( nullptr, databasePath ); } );
2841 }
2842
2843 return functionResult;
2844}
2845
2846static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2847{
2848 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2849 if ( !crs.isValid() )
2850 return QVariant();
2851 return crs.authid();
2852}
2853
2854static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2855{
2856 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2857 QgsCoordinateReferenceSystem crs( definition );
2858
2859 if ( !crs.isValid() )
2860 {
2861 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2862 }
2863
2864 return QVariant::fromValue( crs );
2865}
2866
2867static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2868{
2869 QString concat( "" );
2870 for ( const QVariant &value : values )
2871 {
2872 if ( !QgsVariantUtils::isNull( value ) )
2873 concat += QgsExpressionUtils::getStringValue( value, parent );
2874 }
2875 return concat;
2876}
2877
2878static QVariant fcnConcatWs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2879{
2880 if ( values.length() < 2 )
2881 {
2882 parent->setEvalErrorString( QObject::tr( "Function concat_ws requires at least 2 arguments" ) );
2883 return QVariant();
2884 }
2885
2886 const QString separator = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2887
2888 QStringList stringValues;
2889 stringValues.reserve( values.size() - 1 );
2890 for ( int i = 1; i < values.size(); ++i )
2891 {
2892 const QVariant value = values.at( i );
2893 if ( !QgsVariantUtils::isNull( value ) )
2894 {
2895 stringValues.append( QgsExpressionUtils::getStringValue( value, parent ) );
2896 }
2897 }
2898
2899 return stringValues.join( separator );
2900}
2901
2902static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2903{
2904 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2905 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2906}
2907
2908static QVariant fcnUnaccent( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction *node )
2909{
2910 Q_UNUSED( context )
2911 Q_UNUSED( node )
2912
2913 if ( values.isEmpty() || values[0].isNull() )
2914 return QVariant();
2915
2916 return QgsStringUtils::unaccent( values[0].toString() );
2917}
2918
2919static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2920{
2921 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2922 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2923 return string.right( pos );
2924}
2925
2926static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2927{
2928 if ( values.length() < 2 || values.length() > 3 )
2929 return QVariant();
2930
2931 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2932 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2933
2934 bool overlapping = false;
2935 if ( values.length() == 3 )
2936 {
2937 overlapping = values.at( 2 ).toBool();
2938 }
2939
2940 if ( substring.isEmpty() )
2941 return QVariant( 0 );
2942
2943 int count = 0;
2944 if ( overlapping )
2945 {
2946 count = input.count( substring );
2947 }
2948 else
2949 {
2950 int pos = 0;
2951 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2952 {
2953 count++;
2954 pos += substring.length();
2955 }
2956 }
2957
2958 return QVariant( count );
2959}
2960
2961static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2962{
2963 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2964 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2965 return string.left( pos );
2966}
2967
2968static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2969{
2970 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2971 const int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2972 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2973 if ( fill.isEmpty() )
2974 {
2975 fill = u" "_s;
2976 }
2977 return string.leftJustified( length, fill.at( 0 ), true );
2978}
2979
2980static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2981{
2982 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2983 const int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2984 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2985 if ( fill.isEmpty() )
2986 {
2987 fill = u" "_s;
2988 }
2989 return string.rightJustified( length, fill.at( 0 ), true );
2990}
2991
2992static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2993{
2994 if ( values.size() < 1 )
2995 {
2996 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2997 return QVariant();
2998 }
2999
3000 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3001 for ( int n = 1; n < values.length(); n++ )
3002 {
3003 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
3004 }
3005 return string;
3006}
3007
3008
3009static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3010{
3011 return QVariant( QDateTime::currentDateTime() );
3012}
3013
3014static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3015{
3016 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3017 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3018 if ( format.isEmpty() && !language.isEmpty() )
3019 {
3020 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
3021 return QVariant( QDate() );
3022 }
3023
3024 if ( format.isEmpty() && language.isEmpty() )
3025 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
3026
3027 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3028 QLocale locale = QLocale();
3029 if ( !language.isEmpty() )
3030 {
3031 locale = QLocale( language );
3032 }
3033
3034 QDate date = locale.toDate( datestring, format );
3035 if ( !date.isValid() )
3036 {
3037 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
3038 date = QDate();
3039 }
3040 return QVariant( date );
3041}
3042
3043static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3044{
3045 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3046 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3047 if ( format.isEmpty() && !language.isEmpty() )
3048 {
3049 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
3050 return QVariant( QTime() );
3051 }
3052
3053 if ( format.isEmpty() && language.isEmpty() )
3054 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
3055
3056 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3057 QLocale locale = QLocale();
3058 if ( !language.isEmpty() )
3059 {
3060 locale = QLocale( language );
3061 }
3062
3063 QTime time = locale.toTime( timestring, format );
3064 if ( !time.isValid() )
3065 {
3066 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
3067 time = QTime();
3068 }
3069 return QVariant( time );
3070}
3071
3072static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3073{
3074 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
3075}
3076
3077/*
3078 * DMS functions
3079 */
3080
3081static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3082{
3083 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3084 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3085 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3086
3087 QString formatString;
3088 if ( values.count() > 3 )
3089 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
3090
3092 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
3093 {
3095 }
3096 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
3097 {
3099 }
3100 else if ( !formatString.isEmpty() )
3101 {
3102 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
3103 return QVariant();
3104 }
3105
3106 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
3107 {
3108 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
3109 }
3110 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
3111 {
3112 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
3113 }
3114 else
3115 {
3116 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
3117 return QVariant();
3118 }
3119}
3120
3121static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3122{
3124 return floatToDegreeFormat( format, values, context, parent, node );
3125}
3126
3127static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3128{
3129 double value = 0.0;
3130 bool ok = false;
3131 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
3132
3133 return ok ? QVariant( value ) : QVariant();
3134}
3135
3136static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3137{
3139 return floatToDegreeFormat( format, values, context, parent, node );
3140}
3141
3142static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3143{
3144 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3145 return static_cast< int >( decimalDegrees );
3146}
3147
3148static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3149{
3150 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3151 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3152 return static_cast< int >( remainder * 60 );
3153}
3154
3155static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3156{
3157 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3158 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3159 const double remainderInMinutes = remainder * 60;
3160 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
3161 // do not truncate to int, this function returns decimal seconds!
3162 return remainderSecondsFraction * 60;
3163}
3164
3165static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3166{
3167 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3168 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3169 qint64 seconds = d2.secsTo( d1 );
3170 return QVariant::fromValue( QgsInterval( seconds ) );
3171}
3172
3173static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3174{
3175 if ( !values.at( 0 ).canConvert<QDate>() )
3176 return QVariant();
3177
3178 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
3179 if ( !date.isValid() )
3180 return QVariant();
3181
3182 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
3183 // (to match PostgreSQL behavior)
3184 return date.dayOfWeek() % 7;
3185}
3186
3187static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3188{
3189 QVariant value = values.at( 0 );
3190 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3191 if ( inter.isValid() )
3192 {
3193 return QVariant( inter.days() );
3194 }
3195 else
3196 {
3197 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3198 return QVariant( d1.date().day() );
3199 }
3200}
3201
3202static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3203{
3204 QVariant value = values.at( 0 );
3205 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3206 if ( inter.isValid() )
3207 {
3208 return QVariant( inter.years() );
3209 }
3210 else
3211 {
3212 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3213 return QVariant( d1.date().year() );
3214 }
3215}
3216
3217static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3218{
3219 QVariant value = values.at( 0 );
3220 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3221 if ( inter.isValid() )
3222 {
3223 return QVariant( inter.months() );
3224 }
3225 else
3226 {
3227 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3228 return QVariant( d1.date().month() );
3229 }
3230}
3231
3232static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3233{
3234 QVariant value = values.at( 0 );
3235 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3236 if ( inter.isValid() )
3237 {
3238 return QVariant( inter.weeks() );
3239 }
3240 else
3241 {
3242 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3243 return QVariant( d1.date().weekNumber() );
3244 }
3245}
3246
3247static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3248{
3249 QVariant value = values.at( 0 );
3250 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3251 if ( inter.isValid() )
3252 {
3253 return QVariant( inter.hours() );
3254 }
3255 else
3256 {
3257 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3258 return QVariant( t1.hour() );
3259 }
3260}
3261
3262static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3263{
3264 QVariant value = values.at( 0 );
3265 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3266 if ( inter.isValid() )
3267 {
3268 return QVariant( inter.minutes() );
3269 }
3270 else
3271 {
3272 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3273 return QVariant( t1.minute() );
3274 }
3275}
3276
3277static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3278{
3279 QVariant value = values.at( 0 );
3280 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3281 if ( inter.isValid() )
3282 {
3283 return QVariant( inter.seconds() );
3284 }
3285 else
3286 {
3287 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3288 return QVariant( t1.second() );
3289 }
3290}
3291
3292static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3293{
3294 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3295 if ( dt.isValid() )
3296 {
3297 return QVariant( dt.toMSecsSinceEpoch() );
3298 }
3299 else
3300 {
3301 return QVariant();
3302 }
3303}
3304
3305static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3306{
3307 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3308 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3309 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3310}
3311
3312static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3313{
3314 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3315 if ( parent->hasEvalError() )
3316 {
3317 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3318 return QVariant();
3319 }
3320 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3321 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3322}
3323
3324static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3326 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3327 if ( parent->hasEvalError() )
3328 {
3329 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3330 return QVariant();
3331 }
3332 bool ok;
3333 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3334}
3335
3336double qDateTimeToDecimalYear( const QDateTime &dateTime )
3337{
3338 if ( !dateTime.isValid() )
3339 {
3340 return 0.0;
3341 }
3342
3343 const int year = dateTime.date().year();
3344 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3345 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3346 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3347 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3348 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3349}
3350
3351static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3352{
3353 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3354 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3355 if ( parent->hasEvalError() )
3356 {
3357 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3358 return QVariant();
3359 }
3360 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3361 if ( parent->hasEvalError() )
3362 {
3363 return QVariant();
3364 }
3365 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3366 if ( parent->hasEvalError() )
3367 {
3368 return QVariant();
3369 }
3370 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3371 if ( parent->hasEvalError() )
3372 {
3373 return QVariant();
3374 }
3375 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3376
3377 const QgsMagneticModel model( name, filePath );
3378 try
3379 {
3380 double declination = 0;
3381 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3382 {
3383 return declination;
3384 }
3385 else
3386 {
3387 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3388 }
3389 }
3390 catch ( QgsNotSupportedException &e )
3391 {
3392 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3393 }
3394 return QVariant();
3395}
3396
3397static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3398{
3399 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3400 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3401 if ( parent->hasEvalError() )
3402 {
3403 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3404 return QVariant();
3405 }
3406 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3407 if ( parent->hasEvalError() )
3408 {
3409 return QVariant();
3410 }
3411 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3412 if ( parent->hasEvalError() )
3413 {
3414 return QVariant();
3415 }
3416 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3417 if ( parent->hasEvalError() )
3418 {
3419 return QVariant();
3420 }
3421 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3422
3423 const QgsMagneticModel model( name, filePath );
3424 try
3425 {
3426 double inclination = 0;
3427 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3428 {
3429 return inclination;
3430 }
3431 else
3432 {
3433 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3434 }
3435 }
3436 catch ( QgsNotSupportedException &e )
3437 {
3438 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3439 }
3440 return QVariant();
3441}
3442
3443static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3444{
3445 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3446 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3447 if ( parent->hasEvalError() )
3448 {
3449 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3450 return QVariant();
3451 }
3452 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3453 if ( parent->hasEvalError() )
3454 {
3455 return QVariant();
3456 }
3457 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3458 if ( parent->hasEvalError() )
3459 {
3460 return QVariant();
3461 }
3462 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3463 if ( parent->hasEvalError() )
3464 {
3465 return QVariant();
3466 }
3467 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3468
3469 const QgsMagneticModel model( name, filePath );
3470 try
3471 {
3472 double declination = 0;
3473 double Bx = 0;
3474 double By = 0;
3475 double Bz = 0;
3476 double Bxt = 0;
3477 double Byt = 0;
3478 double Bzt = 0;
3479
3480 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3481 {
3482 double H = 0;
3483 double F = 0;
3484 double D = 0;
3485 double I = 0;
3486 double Ht = 0;
3487 double Ft = 0;
3488 double Dt = 0;
3489 double It = 0;
3490 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3491 {
3492 return Dt;
3493 }
3494 else
3495 {
3496 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3497 }
3498 return declination;
3499 }
3500 else
3501 {
3502 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3503 }
3504 }
3505 catch ( QgsNotSupportedException &e )
3506 {
3507 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3508 }
3509 return QVariant();
3510}
3511
3512static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3513{
3514 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3515 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3516 if ( parent->hasEvalError() )
3517 {
3518 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3519 return QVariant();
3520 }
3521 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3522 if ( parent->hasEvalError() )
3523 {
3524 return QVariant();
3525 }
3526 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3527 if ( parent->hasEvalError() )
3528 {
3529 return QVariant();
3530 }
3531 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3532 if ( parent->hasEvalError() )
3533 {
3534 return QVariant();
3535 }
3536 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3537
3538 const QgsMagneticModel model( name, filePath );
3539 try
3540 {
3541 double declination = 0;
3542 double Bx = 0;
3543 double By = 0;
3544 double Bz = 0;
3545 double Bxt = 0;
3546 double Byt = 0;
3547 double Bzt = 0;
3548
3549 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3550 {
3551 double H = 0;
3552 double F = 0;
3553 double D = 0;
3554 double I = 0;
3555 double Ht = 0;
3556 double Ft = 0;
3557 double Dt = 0;
3558 double It = 0;
3559 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3560 {
3561 return It;
3562 }
3563 else
3564 {
3565 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3566 }
3567 return declination;
3568 }
3569 else
3571 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3572 }
3573 }
3574 catch ( QgsNotSupportedException &e )
3575 {
3576 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3577 }
3578 return QVariant();
3579}
3580
3581#define ENSURE_GEOM_TYPE( f, g, geomtype ) \
3582 if ( !( f ).hasGeometry() ) \
3583 return QVariant(); \
3584 QgsGeometry g = ( f ).geometry(); \
3585 if ( ( g ).type() != ( geomtype ) ) \
3586 return QVariant();
3587
3588static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3589{
3590 FEAT_FROM_CONTEXT( context, f )
3592 if ( g.isMultipart() )
3593 {
3594 return g.asMultiPoint().at( 0 ).x();
3595 }
3596 else
3597 {
3598 return g.asPoint().x();
3599 }
3600}
3601
3602static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3603{
3604 FEAT_FROM_CONTEXT( context, f )
3606 if ( g.isMultipart() )
3607 {
3608 return g.asMultiPoint().at( 0 ).y();
3609 }
3610 else
3611 {
3612 return g.asPoint().y();
3613 }
3614}
3615
3616static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3617{
3618 FEAT_FROM_CONTEXT( context, f )
3620
3621 if ( g.isEmpty() )
3622 return QVariant();
3623
3624 const QgsAbstractGeometry *abGeom = g.constGet();
3625
3626 if ( g.isEmpty() || !abGeom->is3D() )
3627 return QVariant();
3628
3629 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3630 {
3631 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3632 if ( point )
3633 return point->z();
3634 }
3635 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3636 {
3637 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3638 {
3639 if ( collection->numGeometries() > 0 )
3640 {
3641 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3642 return point->z();
3643 }
3644 }
3645 }
3646
3647 return QVariant();
3648}
3649
3650static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3651{
3652 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3653 if ( geom.isNull() )
3654 return QVariant();
3655
3656 bool isValid = geom.isGeosValid();
3657
3658 return QVariant( isValid );
3659}
3660
3661static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3662{
3663 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3664 if ( geom.isNull() )
3665 return QVariant();
3666
3667 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3668#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
3670#else
3672#endif
3673 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3675 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3677
3678 const bool keepCollapsed = values.value( 2 ).toBool();
3679
3680 QgsGeometry valid;
3681 try
3682 {
3683 valid = geom.makeValid( method, keepCollapsed );
3684 }
3685 catch ( QgsNotSupportedException & )
3686 {
3687 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3688 return QVariant();
3689 }
3690
3691 return QVariant::fromValue( valid );
3692}
3693
3694static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3695{
3696 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3697 if ( geom.isNull() )
3698 return QVariant();
3699
3700 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3701 QVariantList array;
3702 for ( int i = 0; i < multiGeom.size(); ++i )
3703 {
3704 array += QVariant::fromValue( multiGeom.at( i ) );
3705 }
3706
3707 return array;
3708}
3709
3710static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3711{
3712 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3713 if ( geom.isNull() )
3714 return QVariant();
3715
3716 //if single point, return the point's x coordinate
3717 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3718 {
3719 return geom.asPoint().x();
3720 }
3721
3722 //otherwise return centroid x
3723 QgsGeometry centroid = geom.centroid();
3724 QVariant result( centroid.asPoint().x() );
3725 return result;
3726}
3727
3728static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3729{
3730 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3731 if ( geom.isNull() )
3732 return QVariant();
3733
3734 //if single point, return the point's y coordinate
3735 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3736 {
3737 return geom.asPoint().y();
3738 }
3739
3740 //otherwise return centroid y
3741 QgsGeometry centroid = geom.centroid();
3742 QVariant result( centroid.asPoint().y() );
3743 return result;
3744}
3745
3746static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3747{
3748 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3749 if ( geom.isNull() )
3750 return QVariant(); //or 0?
3751
3752 if ( !geom.constGet()->is3D() )
3753 return QVariant();
3754
3755 //if single point, return the point's z coordinate
3756 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3757 {
3759 if ( point )
3760 return point->z();
3761 }
3762 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3763 {
3765 {
3766 if ( collection->numGeometries() == 1 )
3767 {
3768 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3769 return point->z();
3770 }
3771 }
3772 }
3773
3774 return QVariant();
3775}
3776
3777static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3778{
3779 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3780 if ( geom.isNull() )
3781 return QVariant(); //or 0?
3782
3783 if ( !geom.constGet()->isMeasure() )
3784 return QVariant();
3785
3786 //if single point, return the point's m value
3787 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3788 {
3790 if ( point )
3791 return point->m();
3792 }
3793 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3794 {
3796 {
3797 if ( collection->numGeometries() == 1 )
3798 {
3799 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3800 return point->m();
3801 }
3802 }
3803 }
3804
3805 return QVariant();
3806}
3807
3808static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3809{
3810 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3811
3812 if ( geom.isNull() )
3813 return QVariant();
3814
3815 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3816
3817 if ( idx < 0 )
3818 {
3819 //negative idx
3820 int count = geom.constGet()->nCoordinates();
3821 idx = count + idx;
3822 }
3823 else
3824 {
3825 //positive idx is 1 based
3826 idx -= 1;
3827 }
3828
3829 QgsVertexId vId;
3830 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3831 {
3832 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3833 return QVariant();
3834 }
3835
3836 QgsPoint point = geom.constGet()->vertexAt( vId );
3837 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3838}
3839
3840static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3841{
3842 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3843
3844 if ( geom.isNull() )
3845 return QVariant();
3846
3847 QgsVertexId vId;
3848 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3849 {
3850 return QVariant();
3851 }
3852
3853 QgsPoint point = geom.constGet()->vertexAt( vId );
3854 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3855}
3856
3857static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3858{
3859 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3860
3861 if ( geom.isNull() )
3862 return QVariant();
3863
3864 QgsVertexId vId;
3865 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3866 {
3867 return QVariant();
3868 }
3869
3870 QgsPoint point = geom.constGet()->vertexAt( vId );
3871 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3872}
3873
3874static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3875{
3876 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3877
3878 if ( geom.isNull() )
3879 return QVariant();
3880
3881 bool ignoreClosing = false;
3882 if ( values.length() > 1 )
3883 {
3884 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3885 }
3886
3887 QgsMultiPoint *mp = new QgsMultiPoint();
3888
3889 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3890 for ( const QgsRingSequence &part : sequence )
3891 {
3892 for ( const QgsPointSequence &ring : part )
3893 {
3894 bool skipLast = false;
3895 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3896 {
3897 skipLast = true;
3898 }
3899
3900 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++i )
3901 {
3902 mp->addGeometry( ring.at( i ).clone() );
3903 }
3904 }
3905 }
3906
3907 return QVariant::fromValue( QgsGeometry( mp ) );
3908}
3909
3910static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3911{
3912 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3913
3914 if ( geom.isNull() )
3915 return QVariant();
3916
3917 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3918
3919 //OK, now we have a complete list of segmentized lines from the geometry
3921 for ( QgsLineString *line : linesToProcess )
3922 {
3923 for ( int i = 0; i < line->numPoints() - 1; ++i )
3924 {
3926 segment->setPoints( QgsPointSequence() << line->pointN( i ) << line->pointN( i + 1 ) );
3927 ml->addGeometry( segment );
3928 }
3929 delete line;
3930 }
3931
3932 return QVariant::fromValue( QgsGeometry( ml ) );
3933}
3934
3935static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3936{
3937 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3938
3939 if ( geom.isNull() )
3940 return QVariant();
3941
3943 if ( !curvePolygon && geom.isMultipart() )
3944 {
3946 {
3947 if ( collection->numGeometries() == 1 )
3948 {
3949 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3950 }
3951 }
3952 }
3953
3954 if ( !curvePolygon )
3955 return QVariant();
3956
3957 //idx is 1 based
3958 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3959
3960 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3961 return QVariant();
3962
3963 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3964 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3965 return result;
3966}
3967
3968static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3969{
3970 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3971
3972 if ( geom.isNull() )
3973 return QVariant();
3974
3976 if ( !collection )
3977 return QVariant();
3978
3979 //idx is 1 based
3980 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3981
3982 if ( idx < 0 || idx >= collection->numGeometries() )
3983 return QVariant();
3984
3985 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3986 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3987 return result;
3988}
3989
3990static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3991{
3992 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3993
3994 if ( geom.isNull() )
3995 return QVariant();
3996
3997 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3998 if ( !boundary )
3999 return QVariant();
4000
4001 return QVariant::fromValue( QgsGeometry( boundary ) );
4002}
4003
4004static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4005{
4006 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4007
4008 if ( geom.isNull() )
4009 return QVariant();
4010
4011 QgsGeometry merged = geom.mergeLines();
4012 if ( merged.isNull() )
4013 return QVariant();
4014
4015 return QVariant::fromValue( merged );
4016}
4017
4018static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4019{
4020 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4021 if ( geom.isNull() )
4022 return QVariant();
4023
4024 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4025 if ( geom2.isNull() )
4026 return QVariant();
4027
4028 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
4029 if ( sharedPaths.isNull() )
4030 return QVariant();
4031
4032 return QVariant::fromValue( sharedPaths );
4033}
4034
4035
4036static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4037{
4038 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4039
4040 if ( geom.isNull() )
4041 return QVariant();
4042
4043 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4044
4045 QgsGeometry simplified = geom.simplify( tolerance );
4046 if ( simplified.isNull() )
4047 return QVariant();
4048
4049 return simplified;
4050}
4051
4052static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4053{
4054 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4055
4056 if ( geom.isNull() )
4057 return QVariant();
4058
4059 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4060
4062
4063 QgsGeometry simplified = simplifier.simplify( geom );
4064 if ( simplified.isNull() )
4065 return QVariant();
4066
4067 return simplified;
4068}
4069
4070static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4071{
4072 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4073
4074 if ( geom.isNull() )
4075 return QVariant();
4076
4077 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
4078 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
4079 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4080 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
4081
4082 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
4083 if ( smoothed.isNull() )
4084 return QVariant();
4085
4086 return smoothed;
4087}
4088
4089static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4090{
4091 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4092
4093 if ( geom.isNull() )
4094 return QVariant();
4095
4096 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4097 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4098 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4099
4100 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
4101 if ( waved.isNull() )
4102 return QVariant();
4103
4104 return waved;
4105}
4106
4107static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4108{
4109 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4110
4111 if ( geom.isNull() )
4112 return QVariant();
4113
4114 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4115 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4116 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4117 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4118 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4119
4120 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4121 if ( waved.isNull() )
4122 return QVariant();
4123
4124 return waved;
4125}
4126
4127static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4128{
4129 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4130
4131 if ( geom.isNull() )
4132 return QVariant();
4133
4134 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4135 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4136 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4137
4138 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
4139 if ( waved.isNull() )
4140 return QVariant();
4141
4142 return waved;
4143}
4144
4145static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4146{
4147 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4148
4149 if ( geom.isNull() )
4150 return QVariant();
4151
4152 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4153 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4154 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4155 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4156 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4157
4158 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4159 if ( waved.isNull() )
4160 return QVariant();
4161
4162 return waved;
4163}
4164
4165static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4166{
4167 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4168
4169 if ( geom.isNull() )
4170 return QVariant();
4171
4172 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4173 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4174 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4175
4176 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
4177 if ( waved.isNull() )
4178 return QVariant();
4179
4180 return waved;
4181}
4182
4183static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4184{
4185 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4186
4187 if ( geom.isNull() )
4188 return QVariant();
4189
4190 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4191 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4192 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4193 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4194 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4195
4196 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4197 if ( waved.isNull() )
4198 return QVariant();
4199
4200 return waved;
4201}
4202
4203static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4204{
4205 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4206
4207 if ( geom.isNull() )
4208 return QVariant();
4209
4210 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4211 QVector< double > dashPattern;
4212 dashPattern.reserve( pattern.size() );
4213 for ( const QVariant &value : std::as_const( pattern ) )
4214 {
4215 bool ok = false;
4216 double v = value.toDouble( &ok );
4217 if ( ok )
4218 {
4219 dashPattern << v;
4220 }
4221 else
4222 {
4223 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4224 return QgsGeometry();
4225 }
4226 }
4227
4228 if ( dashPattern.size() % 2 != 0 )
4229 {
4230 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4231 return QgsGeometry();
4232 }
4233
4234 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4236 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4238 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4240 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4242 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4244 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4246 else
4247 {
4248 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4249 return QgsGeometry();
4250 }
4251
4252 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4254 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4256 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4258 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4260 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4262 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4264 else
4265 {
4266 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4267 return QgsGeometry();
4268 }
4269
4270 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4272 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4274 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4276 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4278 else
4279 {
4280 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4281 return QgsGeometry();
4282 }
4283
4284 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4285
4286 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4287 if ( result.isNull() )
4288 return QVariant();
4289
4290 return result;
4291}
4292
4293static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4294{
4295 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4296
4297 if ( geom.isNull() )
4298 return QVariant();
4299
4300 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4301 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4302 if ( densified.isNull() )
4303 return QVariant();
4304
4305 return densified;
4306}
4307
4308static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4309{
4310 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4311
4312 if ( geom.isNull() )
4313 return QVariant();
4314
4315 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4316 const QgsGeometry densified = geom.densifyByDistance( distance );
4317 if ( densified.isNull() )
4318 return QVariant();
4319
4320 return densified;
4321}
4322
4323static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4324{
4325 QVariantList list;
4326 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4327 {
4328 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4329 }
4330 else
4331 {
4332 list = values;
4333 }
4334
4335 QVector< QgsGeometry > parts;
4336 parts.reserve( list.size() );
4337 for ( const QVariant &value : std::as_const( list ) )
4338 {
4339 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4340 if ( part.isNull() )
4341 return QgsGeometry();
4342 parts << part;
4343 }
4344
4345 return QgsGeometry::collectGeometry( parts );
4346}
4347
4348static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4349{
4350 if ( values.count() < 2 || values.count() > 4 )
4351 {
4352 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4353 return QVariant();
4354 }
4355
4356 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4357 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4358 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4359 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4360 switch ( values.count() )
4361 {
4362 case 2:
4363 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4364 case 3:
4365 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4366 case 4:
4367 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4368 }
4369 return QVariant(); //avoid warning
4370}
4371
4372static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4373{
4374 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4375 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4376 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4377 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4378}
4379
4380static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4381{
4382 if ( values.empty() )
4383 {
4384 return QVariant();
4385 }
4386
4387 QVector<QgsPoint> points;
4388 points.reserve( values.count() );
4389
4390 auto addPoint = [&points]( const QgsGeometry &geom ) {
4391 if ( geom.isNull() )
4392 return;
4393
4394 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4395 return;
4396
4398 if ( !point )
4399 return;
4400
4401 points << *point;
4402 };
4403
4404 for ( const QVariant &value : values )
4405 {
4406 if ( value.userType() == QMetaType::Type::QVariantList )
4407 {
4408 const QVariantList list = value.toList();
4409 for ( const QVariant &v : list )
4410 {
4411 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4412 }
4413 }
4414 else
4415 {
4416 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4417 }
4418 }
4419
4420 if ( points.count() < 2 )
4421 return QVariant();
4422
4423 return QgsGeometry( new QgsLineString( points ) );
4424}
4425
4426static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4427{
4428 if ( values.count() < 1 )
4429 {
4430 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4431 return QVariant();
4432 }
4433
4434 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4435
4436 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4437 return outerRing; // if it's already a polygon we have nothing to do
4438
4439 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4440 return QVariant();
4441
4442 auto polygon = std::make_unique< QgsPolygon >();
4443
4444 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4445 if ( !exteriorRing && outerRing.isMultipart() )
4446 {
4448 {
4449 if ( collection->numGeometries() == 1 )
4450 {
4451 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4452 }
4453 }
4454 }
4455
4456 if ( !exteriorRing )
4457 return QVariant();
4458
4459 polygon->setExteriorRing( exteriorRing->segmentize() );
4460
4461
4462 for ( int i = 1; i < values.count(); ++i )
4463 {
4464 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4465 if ( ringGeom.isNull() )
4466 continue;
4467
4468 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4469 continue;
4470
4471 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4472 if ( !ring && ringGeom.isMultipart() )
4473 {
4475 {
4476 if ( collection->numGeometries() == 1 )
4477 {
4478 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4479 }
4480 }
4481 }
4482
4483 if ( !ring )
4484 continue;
4485
4486 polygon->addInteriorRing( ring->segmentize() );
4487 }
4488
4489 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4490}
4491
4492static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4493{
4494 auto tr = std::make_unique<QgsTriangle>();
4495 auto lineString = std::make_unique<QgsLineString>();
4496 lineString->clear();
4497
4498 for ( const QVariant &value : values )
4499 {
4500 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4501 if ( geom.isNull() )
4502 return QVariant();
4503
4504 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4505 return QVariant();
4506
4508 if ( !point && geom.isMultipart() )
4509 {
4511 {
4512 if ( collection->numGeometries() == 1 )
4513 {
4514 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4515 }
4516 }
4517 }
4518
4519 if ( !point )
4520 return QVariant();
4521
4522 lineString->addVertex( *point );
4523 }
4524
4525 tr->setExteriorRing( lineString.release() );
4526
4527 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4528}
4529
4530static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4531{
4532 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4533 if ( geom.isNull() )
4534 return QVariant();
4535
4536 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4537 return QVariant();
4538
4539 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4540 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4541
4542 if ( segment < 3 )
4543 {
4544 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4545 return QVariant();
4546 }
4548 if ( !point && geom.isMultipart() )
4549 {
4551 {
4552 if ( collection->numGeometries() == 1 )
4553 {
4554 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4555 }
4556 }
4557 }
4558 if ( !point )
4559 return QVariant();
4560
4561 QgsCircle circ( *point, radius );
4562 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4563}
4564
4565static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4566{
4567 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4568 if ( geom.isNull() )
4569 return QVariant();
4570
4571 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4572 return QVariant();
4573
4574 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4575 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4576 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4577 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4578 if ( segment < 3 )
4579 {
4580 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4581 return QVariant();
4582 }
4584 if ( !point && geom.isMultipart() )
4585 {
4587 {
4588 if ( collection->numGeometries() == 1 )
4589 {
4590 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4591 }
4592 }
4593 }
4594 if ( !point )
4595 return QVariant();
4596
4597 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4598 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4599}
4600
4601static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4602{
4603 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4604 if ( pt1.isNull() )
4605 return QVariant();
4606
4607 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4608 return QVariant();
4609
4610 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4611 if ( pt2.isNull() )
4612 return QVariant();
4613
4614 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4615 return QVariant();
4616
4617 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4618 if ( nbEdges < 3 )
4619 {
4620 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4621 return QVariant();
4622 }
4623
4624 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4626 {
4627 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4628 return QVariant();
4629 }
4630
4632 if ( !center && pt1.isMultipart() )
4633 {
4635 {
4636 if ( collection->numGeometries() == 1 )
4637 {
4638 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4639 }
4640 }
4641 }
4642 if ( !center )
4643 return QVariant();
4644
4646 if ( !corner && pt2.isMultipart() )
4647 {
4649 {
4650 if ( collection->numGeometries() == 1 )
4651 {
4652 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4653 }
4654 }
4655 }
4656 if ( !corner )
4657 return QVariant();
4658
4659 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4660
4661 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4662}
4663
4664static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4665{
4666 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4667 if ( pt1.isNull() )
4668 return QVariant();
4669 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4670 return QVariant();
4671
4672 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4673 if ( pt2.isNull() )
4674 return QVariant();
4675 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4676 return QVariant();
4677
4678 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4679 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4680 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4681
4682 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4683}
4684
4685static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4686{
4687 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4688 if ( pt1.isNull() )
4689 return QVariant();
4690 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4691 return QVariant();
4692
4693 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4694 if ( pt2.isNull() )
4695 return QVariant();
4696 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4697 return QVariant();
4698
4699 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4700 if ( pt3.isNull() )
4701 return QVariant();
4702 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4703 return QVariant();
4704
4705 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4706 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4707 {
4708 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4709 return QVariant();
4710 }
4711 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4712 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4713 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4714 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4715 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4716}
4717
4718static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4719{
4720 if ( geom.isNull() )
4721 return QVariant();
4722
4723 if ( idx < 0 )
4724 {
4725 idx += geom.constGet()->nCoordinates();
4726 }
4727 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4728 {
4729 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4730 return QVariant();
4731 }
4732 return QVariant::fromValue( geom.vertexAt( idx ) );
4733}
4734
4735// function used for the old $ style
4736static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4737{
4738 FEAT_FROM_CONTEXT( context, feature )
4739 const QgsGeometry geom = feature.geometry();
4740 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4741
4742 const QVariant v = pointAt( geom, idx, parent );
4743
4744 if ( !v.isNull() )
4745 return QVariant( v.value<QgsPoint>().x() );
4746 else
4747 return QVariant();
4748}
4749static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4750{
4751 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))
4752 {
4753 return fcnOldXat( values, f, parent, node );
4754 }
4755 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)
4756 {
4757 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4758 }
4759
4760 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4761 if ( geom.isNull() )
4762 {
4763 return QVariant();
4764 }
4765
4766 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4767
4768 const QVariant v = pointAt( geom, vertexNumber, parent );
4769 if ( !v.isNull() )
4770 return QVariant( v.value<QgsPoint>().x() );
4771 else
4772 return QVariant();
4773}
4774
4775// function used for the old $ style
4776static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4777{
4778 FEAT_FROM_CONTEXT( context, feature )
4779 const QgsGeometry geom = feature.geometry();
4780 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4781
4782 const QVariant v = pointAt( geom, idx, parent );
4783
4784 if ( !v.isNull() )
4785 return QVariant( v.value<QgsPoint>().y() );
4786 else
4787 return QVariant();
4788}
4789static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4790{
4791 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))
4792 {
4793 return fcnOldYat( values, f, parent, node );
4794 }
4795 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)
4796 {
4797 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4798 }
4799
4800 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4801 if ( geom.isNull() )
4802 {
4803 return QVariant();
4804 }
4805
4806 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4807
4808 const QVariant v = pointAt( geom, vertexNumber, parent );
4809 if ( !v.isNull() )
4810 return QVariant( v.value<QgsPoint>().y() );
4811 else
4812 return QVariant();
4813}
4814
4815static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4816{
4817 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4818 if ( geom.isNull() )
4819 {
4820 return QVariant();
4821 }
4822
4823 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4824
4825 const QVariant v = pointAt( geom, vertexNumber, parent );
4826 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4827 return QVariant( v.value<QgsPoint>().z() );
4828 else
4829 return QVariant();
4830}
4831
4832static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4833{
4834 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4835 if ( geom.isNull() )
4836 {
4837 return QVariant();
4838 }
4839
4840 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4841
4842 const QVariant v = pointAt( geom, vertexNumber, parent );
4843 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4844 return QVariant( v.value<QgsPoint>().m() );
4845 else
4846 return QVariant();
4847}
4848
4849
4850static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4851{
4852 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4853 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4854 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4855 return result;
4856}
4857
4858static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4859{
4860 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4861 if ( wkb.isNull() )
4862 return QVariant();
4863
4864 QgsGeometry geom;
4865 geom.fromWkb( wkb );
4866 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4867}
4868
4869static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4870{
4871 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4872 QgsOgcUtils::Context ogcContext;
4873 if ( context )
4874 {
4875 QgsWeakMapLayerPointer mapLayerPtr { context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4876 if ( mapLayerPtr )
4877 {
4878 ogcContext.layer = mapLayerPtr.data();
4879 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4880 }
4881 }
4882 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4883 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4884 return result;
4885}
4886
4887static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4888{
4889 FEAT_FROM_CONTEXT( context, f )
4891 QgsDistanceArea *calc = parent->geomCalculator();
4892 if ( calc )
4893 {
4894 try
4895 {
4896 double area = calc->measureArea( f.geometry() );
4897 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4898 return QVariant( area );
4899 }
4900 catch ( QgsCsException & )
4901 {
4902 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4903 return QVariant();
4904 }
4905 }
4906 else
4907 {
4908 return QVariant( f.geometry().area() );
4909 }
4910}
4911
4912static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4913{
4914 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4915
4916 if ( geom.type() != Qgis::GeometryType::Polygon )
4917 return QVariant();
4918
4919 return QVariant( geom.area() );
4920}
4921
4922static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4923{
4924 FEAT_FROM_CONTEXT( context, f )
4926 QgsDistanceArea *calc = parent->geomCalculator();
4927 if ( calc )
4928 {
4929 try
4930 {
4931 double len = calc->measureLength( f.geometry() );
4932 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4933 return QVariant( len );
4934 }
4935 catch ( QgsCsException & )
4936 {
4937 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4938 return QVariant();
4939 }
4940 }
4941 else
4942 {
4943 return QVariant( f.geometry().length() );
4944 }
4945}
4946
4947static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4948{
4949 FEAT_FROM_CONTEXT( context, f )
4951 QgsDistanceArea *calc = parent->geomCalculator();
4952 if ( calc )
4953 {
4954 try
4955 {
4956 double len = calc->measurePerimeter( f.geometry() );
4957 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4958 return QVariant( len );
4959 }
4960 catch ( QgsCsException & )
4961 {
4962 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4963 return QVariant();
4964 }
4965 }
4966 else
4967 {
4968 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4969 }
4970}
4971
4972static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4973{
4974 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4975
4976 if ( geom.type() != Qgis::GeometryType::Polygon )
4977 return QVariant();
4978
4979 //length for polygons = perimeter
4980 return QVariant( geom.length() );
4981}
4982
4983static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4984{
4985 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4986 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4987}
4988
4989static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4990{
4991 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4992 if ( geom.isNull() )
4993 return QVariant();
4994
4995 return QVariant( geom.constGet()->partCount() );
4996}
4997
4998static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4999{
5000 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5001 if ( geom.isNull() )
5002 return QVariant();
5003
5004 return QVariant( geom.isMultipart() );
5005}
5006
5007static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5008{
5009 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5010
5011 if ( geom.isNull() )
5012 return QVariant();
5013
5015 if ( curvePolygon )
5016 return QVariant( curvePolygon->numInteriorRings() );
5017
5019 if ( collection )
5020 {
5021 //find first CurvePolygon in collection
5022 for ( int i = 0; i < collection->numGeometries(); ++i )
5023 {
5024 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5025 if ( !curvePolygon )
5026 continue;
5027
5028 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
5029 }
5030 }
5031
5032 return QVariant();
5033}
5034
5035static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5036{
5037 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5038
5039 if ( geom.isNull() )
5040 return QVariant();
5041
5043 if ( curvePolygon )
5044 return QVariant( curvePolygon->ringCount() );
5045
5046 bool foundPoly = false;
5047 int ringCount = 0;
5049 if ( collection )
5050 {
5051 //find CurvePolygons in collection
5052 for ( int i = 0; i < collection->numGeometries(); ++i )
5053 {
5054 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5055 if ( !curvePolygon )
5056 continue;
5057
5058 foundPoly = true;
5059 ringCount += curvePolygon->ringCount();
5060 }
5061 }
5062
5063 if ( !foundPoly )
5064 return QVariant();
5065
5066 return QVariant( ringCount );
5067}
5068
5069static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5070{
5071 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5072 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
5073 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
5074 return result;
5075}
5076
5077static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5078{
5079 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5080 return QVariant::fromValue( geom.boundingBox().width() );
5081}
5082
5083static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5084{
5085 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5086 return QVariant::fromValue( geom.boundingBox().height() );
5087}
5088
5089static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5090{
5091 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5092 if ( geom.isNull() )
5093 return QVariant();
5094
5096}
5097
5098static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5099{
5100 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5101 return QVariant::fromValue( geom.boundingBox().xMinimum() );
5102}
5103
5104static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5105{
5106 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5107 return QVariant::fromValue( geom.boundingBox().xMaximum() );
5108}
5109
5110static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5111{
5112 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5113 return QVariant::fromValue( geom.boundingBox().yMinimum() );
5114}
5115
5116static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5117{
5118 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5119 return QVariant::fromValue( geom.boundingBox().yMaximum() );
5120}
5121
5122static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5123{
5124 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5125
5126 if ( geom.isNull() || geom.isEmpty() )
5127 return QVariant();
5128
5129 if ( !geom.constGet()->is3D() )
5130 return QVariant();
5131
5132 double max = std::numeric_limits< double >::lowest();
5133
5134 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5135 {
5136 double z = ( *it ).z();
5137
5138 if ( max < z )
5139 max = z;
5140 }
5141
5142 if ( max == std::numeric_limits< double >::lowest() )
5143 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5144
5145 return QVariant( max );
5146}
5147
5148static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5149{
5150 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5151
5152 if ( geom.isNull() || geom.isEmpty() )
5153 return QVariant();
5154
5155 if ( !geom.constGet()->is3D() )
5156 return QVariant();
5157
5158 double min = std::numeric_limits< double >::max();
5159
5160 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5161 {
5162 double z = ( *it ).z();
5163
5164 if ( z < min )
5165 min = z;
5166 }
5167
5168 if ( min == std::numeric_limits< double >::max() )
5169 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5170
5171 return QVariant( min );
5172}
5173
5174static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5175{
5176 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5177
5178 if ( geom.isNull() || geom.isEmpty() )
5179 return QVariant();
5180
5181 if ( !geom.constGet()->isMeasure() )
5182 return QVariant();
5183
5184 double min = std::numeric_limits< double >::max();
5185
5186 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5187 {
5188 double m = ( *it ).m();
5189
5190 if ( m < min )
5191 min = m;
5192 }
5193
5194 if ( min == std::numeric_limits< double >::max() )
5195 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5196
5197 return QVariant( min );
5198}
5199
5200static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5201{
5202 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5203
5204 if ( geom.isNull() || geom.isEmpty() )
5205 return QVariant();
5206
5207 if ( !geom.constGet()->isMeasure() )
5208 return QVariant();
5209
5210 double max = std::numeric_limits< double >::lowest();
5211
5212 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5213 {
5214 double m = ( *it ).m();
5215
5216 if ( max < m )
5217 max = m;
5218 }
5219
5220 if ( max == std::numeric_limits< double >::lowest() )
5221 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5222
5223 return QVariant( max );
5224}
5225
5226static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5227{
5228 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5230 if ( !curve )
5231 {
5232 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5233 return QVariant();
5234 }
5235
5236 return QVariant( curve->sinuosity() );
5237}
5238
5239static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5240{
5241 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5242 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5243 if ( !curve )
5244 {
5245 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5246 return QVariant();
5247 }
5248
5249 return QVariant( curve->straightDistance2d() );
5250}
5251
5252static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5253{
5254 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5256
5257 if ( !poly )
5258 {
5259 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5260 return QVariant();
5261 }
5262
5263 return QVariant( poly->roundness() );
5264}
5265
5266
5267static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5268{
5269 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5270 if ( geom.isNull() )
5271 return QVariant();
5272
5273 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5274 flipped->swapXy();
5275 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5276}
5277
5278static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5279{
5280 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5281 if ( fGeom.isNull() )
5282 return QVariant();
5283
5284 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5285 if ( !curve && fGeom.isMultipart() )
5286 {
5288 {
5289 if ( collection->numGeometries() == 1 )
5290 {
5291 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5292 }
5293 }
5294 }
5295
5296 if ( !curve )
5297 return QVariant();
5298
5299 return QVariant::fromValue( curve->isClosed() );
5300}
5301
5302static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5303{
5304 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5305
5306 if ( geom.isNull() )
5307 return QVariant();
5308
5309 QVariant result;
5310 if ( !geom.isMultipart() )
5311 {
5313
5314 if ( !line )
5315 return QVariant();
5316
5317 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5318 closedLine->close();
5319
5320 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5321 }
5322 else
5323 {
5325 if ( !collection )
5326 return QVariant();
5327
5328 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5329
5330 for ( int i = 0; i < collection->numGeometries(); ++i )
5331 {
5332 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5333 {
5334 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5335 closedLine->close();
5336
5337 closed->addGeometry( closedLine.release() );
5338 }
5339 }
5340 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5341 }
5342
5343 return result;
5344}
5345
5346static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5347{
5348 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5349 if ( fGeom.isNull() )
5350 return QVariant();
5351
5352 return QVariant::fromValue( fGeom.isEmpty() );
5353}
5354
5355static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5356{
5357 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5358 return QVariant::fromValue( true );
5359
5360 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5361 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5362}
5363
5364static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5365{
5366 if ( values.length() < 2 || values.length() > 3 )
5367 return QVariant();
5368
5369 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5370 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5371
5372 if ( fGeom.isNull() || sGeom.isNull() )
5373 return QVariant();
5374
5375 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5376
5377 if ( values.length() == 2 )
5378 {
5379 //two geometry arguments, return relation
5380 QString result = engine->relate( sGeom.constGet() );
5381 return QVariant::fromValue( result );
5382 }
5383 else
5384 {
5385 //three arguments, test pattern
5386 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5387 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5388 return QVariant::fromValue( result );
5389 }
5390}
5391
5392static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5393{
5394 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5395 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5396 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5397}
5398static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5399{
5400 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5401 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5402 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5403}
5404static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5405{
5406 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5407 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5408 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5409}
5410static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5411{
5412 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5413 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5414 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5415}
5416static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5417{
5418 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5419 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5420 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5421}
5422static QVariant fcnContains( 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.contains( sGeom ) ? TVL_True : TVL_False;
5427}
5428static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5429{
5430 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5431 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5432 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5433}
5434static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5435{
5436 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5437 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5438 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5439}
5440
5441static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5442{
5443 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5444 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5445 return fGeom.isExactlyEqual( sGeom ) ? TVL_True : TVL_False;
5446}
5447
5448static QVariant fcnIsEqualsExact( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5449{
5450 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5451 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5452 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5453
5454 bool ok;
5455 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5456 if ( !ok )
5457 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5458
5459 QVariant ret = TVL_False;
5460 try
5461 {
5462 ret = fGeom.isExactlyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5463 }
5464 catch ( QgsNotSupportedException &e )
5465 {
5466 SET_EVAL_ERROR( e.what() );
5467 }
5468 return ret;
5469}
5470
5471static QVariant fcnIsEqualsTopological( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5472{
5473 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5474 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5475 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5476
5477 bool ok;
5478 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
5479 if ( !ok )
5480 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5481
5482 QVariant ret = TVL_False;
5483 try
5484 {
5485 ret = fGeom.isTopologicallyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5486 }
5487 catch ( QgsNotSupportedException &e )
5488 {
5489 SET_EVAL_ERROR( e.what() );
5490 }
5491 return ret;
5492}
5493
5494static QVariant fcnIsEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5495{
5496 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5497 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5498 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5499
5500 bool ok;
5501 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5502 if ( !ok )
5503 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5504
5505 double epsilon = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5506
5507 QVariant ret = TVL_False;
5508 try
5509 {
5510 ret = fGeom.isFuzzyEqual( sGeom, epsilon, backend ) ? TVL_True : TVL_False;
5511 }
5512 catch ( QgsNotSupportedException &e )
5513 {
5514 SET_EVAL_ERROR( e.what() );
5515 }
5516 return ret;
5517}
5518
5519static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5520{
5521 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5522 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5523 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5524 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5525 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5526 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5527
5529 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5530 capStyle = Qgis::EndCapStyle::Flat;
5531 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5532 capStyle = Qgis::EndCapStyle::Square;
5533
5535 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5536 joinStyle = Qgis::JoinStyle::Miter;
5537 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5538 joinStyle = Qgis::JoinStyle::Bevel;
5539
5540 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5541 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5542 return result;
5543}
5544
5545static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5546{
5547 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5548 const QgsGeometry reoriented = fGeom.forceRHR();
5549 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5550}
5551
5552static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5553{
5554 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5555 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5556 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5557}
5558
5559static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5560{
5561 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5562 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5563 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5564}
5565
5566static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5567{
5568 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5570 if ( !pt && fGeom.isMultipart() )
5571 {
5573 {
5574 if ( collection->numGeometries() == 1 )
5575 {
5576 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5577 }
5578 }
5579 }
5580
5581 if ( !pt )
5582 {
5583 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5584 return QVariant();
5585 }
5586
5587 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5588 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5589 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5590 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5591
5592 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5593 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5594 return result;
5595}
5596
5597static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5598{
5599 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5600 if ( fGeom.type() != Qgis::GeometryType::Line )
5601 {
5602 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5603 return QVariant();
5604 }
5605
5606 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5607 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5608 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5609
5610 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5611 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5612 return result;
5613}
5614
5615static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5616{
5617 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5618 if ( fGeom.type() != Qgis::GeometryType::Line )
5619 {
5620 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5621 return QVariant();
5622 }
5623
5624 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5625
5626 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5627 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5628 return result;
5629}
5630
5631static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5632{
5633 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5634 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5635 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5636 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5637 if ( joinInt < 1 || joinInt > 3 )
5638 return QVariant();
5639 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5640
5641 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5642
5643 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5644 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5645 return result;
5646}
5647
5648static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5649{
5650 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5651 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5652 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5653
5654 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5655 if ( joinInt < 1 || joinInt > 3 )
5656 return QVariant();
5657 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5658
5659 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5660
5661 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5662 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5663 return result;
5664}
5665
5666static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5667{
5668 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5669 const double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5670 const double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5671
5672 const double deflectionStart = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5673 const double deflectionEnd = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5674
5675 QgsGeometry geom = fGeom.extendLine( distStart, distEnd, deflectionStart, deflectionEnd );
5676 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5677 return result;
5678}
5679
5680static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5681{
5682 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5683 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5684 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5685 fGeom.translate( dx, dy );
5686 return QVariant::fromValue( fGeom );
5687}
5688
5689static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5690{
5691 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5692 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5693 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent ) : QgsGeometry();
5694 const bool perPart = values.value( 3 ).toBool();
5695
5696 if ( center.isNull() && perPart && fGeom.isMultipart() )
5697 {
5698 // no explicit center, rotating per part
5699 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5700 // the result is equivalent to setting perPart as false anyway)
5701 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5702 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5703 {
5704 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5705 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5706 t.rotate( -rotation );
5707 t.translate( -partCenter.x(), -partCenter.y() );
5708 ( *it )->transform( t );
5709 }
5710 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5711 }
5712 else
5713 {
5714 QgsPointXY pt;
5715 if ( center.isEmpty() )
5716 {
5717 // if center wasn't specified, use bounding box centroid
5718 pt = fGeom.boundingBox().center();
5719 }
5721 {
5722 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5723 return QVariant();
5724 }
5725 else
5726 {
5728 }
5729
5730 fGeom.rotate( rotation, pt );
5731 return QVariant::fromValue( fGeom );
5732 }
5733}
5734
5735static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5736{
5737 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5738 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5739 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5740 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent ) : QgsGeometry();
5741
5742 QgsPointXY pt;
5743 if ( center.isNull() )
5744 {
5745 // if center wasn't specified, use bounding box centroid
5746 pt = fGeom.boundingBox().center();
5747 }
5749 {
5750 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5751 return QVariant();
5752 }
5753 else
5754 {
5755 pt = center.asPoint();
5756 }
5757
5758 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5759 t.scale( xScale, yScale );
5760 t.translate( -pt.x(), -pt.y() );
5761 fGeom.transform( t );
5762 return QVariant::fromValue( fGeom );
5763}
5764
5765static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5766{
5767 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5768 if ( fGeom.isNull() )
5769 {
5770 return QVariant();
5771 }
5772
5773 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5774 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5775
5776 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5777
5778 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5779 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5780
5781 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5782 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5783 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5784 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5785
5786 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5787 {
5788 fGeom.get()->addZValue( 0 );
5789 }
5790 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5791 {
5792 fGeom.get()->addMValue( 0 );
5793 }
5794
5795 QTransform transform;
5796 transform.translate( deltaX, deltaY );
5797 transform.rotate( rotationZ );
5798 transform.scale( scaleX, scaleY );
5799 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5800
5801 return QVariant::fromValue( fGeom );
5802}
5803
5804
5805static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5806{
5807 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5808 QgsGeometry geom = fGeom.centroid();
5809 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5810 return result;
5811}
5812static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5813{
5814 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5815 QgsGeometry geom = fGeom.pointOnSurface();
5816 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5817 return result;
5818}
5819
5820static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5821{
5822 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5823 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5824 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5825 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5826 return result;
5827}
5828
5829static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5830{
5831 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5832 QgsGeometry geom = fGeom.convexHull();
5833 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5834 return result;
5835}
5836
5837#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
5838static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5839{
5840 try
5841 {
5842 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5843 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5844 const bool allowHoles = values.value( 2 ).toBool();
5845 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5846 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5847 return result;
5848 }
5849 catch ( QgsCsException &cse )
5850 {
5851 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5852 return QVariant();
5853 }
5854}
5855#endif
5856
5857static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5858{
5859 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5860 int segments = 36;
5861 if ( values.length() == 2 )
5862 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5863 if ( segments < 0 )
5864 {
5865 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5866 return QVariant();
5867 }
5868
5869 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5870 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5871 return result;
5872}
5873
5874static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5875{
5876 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5878 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5879 return result;
5880}
5881
5882static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5883{
5884 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5885
5886 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5887 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5888 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5889
5890 double area, angle, width, height;
5891 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5892
5893 if ( geom.isNull() )
5894 {
5895 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5896 return QVariant();
5897 }
5898 return angle;
5899}
5900
5901static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5902{
5903 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5904 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5905 QgsGeometry geom = fGeom.difference( sGeom );
5906 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5907 return result;
5908}
5909
5910static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5911{
5912 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5913 return QVariant();
5914
5915 // two variants, one for geometry, one for string
5916
5917 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5918 if ( !fGeom.isNull() )
5919 {
5920 QVariant result;
5921 if ( !fGeom.isMultipart() )
5922 {
5923 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5924 if ( !curve )
5925 return QVariant();
5926
5927 QgsCurve *reversed = curve->reversed();
5928 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5929 }
5930 else
5931 {
5933 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5934 for ( int i = 0; i < collection->numGeometries(); ++i )
5935 {
5936 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5937 {
5938 reversed->addGeometry( curve->reversed() );
5939 }
5940 else
5941 {
5942 reversed->addGeometry( collection->geometryN( i )->clone() );
5943 }
5944 }
5945 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5946 }
5947 return result;
5948 }
5949
5950 //fall back to string variant
5951 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5952 std::reverse( string.begin(), string.end() );
5953 return string;
5954}
5955
5956static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5957{
5958 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5959 if ( fGeom.isNull() )
5960 return QVariant();
5961
5963 if ( !curvePolygon && fGeom.isMultipart() )
5964 {
5966 {
5967 if ( collection->numGeometries() == 1 )
5968 {
5969 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5970 }
5971 }
5972 }
5973
5974 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5975 return QVariant();
5976
5977 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5978 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5979 return result;
5980}
5981
5982static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5983{
5984 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5985 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5986 return QVariant( fGeom.distance( sGeom ) );
5987}
5988
5989static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5990{
5991 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5992 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5993
5994 double res = -1;
5995 if ( values.length() == 3 && values.at( 2 ).isValid() )
5996 {
5997 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5998 densify = std::clamp( densify, 0.0, 1.0 );
5999 res = g1.hausdorffDistanceDensify( g2, densify );
6000 }
6001 else
6002 {
6003 res = g1.hausdorffDistance( g2 );
6004 }
6005
6006 return res > -1 ? QVariant( res ) : QVariant();
6007}
6008
6009static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6010{
6011 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6012 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6013 QgsGeometry geom = fGeom.intersection( sGeom );
6014 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6015 return result;
6016}
6017static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6018{
6019 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6020 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6021 QgsGeometry geom = fGeom.symDifference( sGeom );
6022 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6023 return result;
6024}
6025static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6026{
6027 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6028 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6029 QgsGeometry geom = fGeom.combine( sGeom );
6030 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6031 return result;
6032}
6033
6034static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6035{
6036 if ( values.length() < 1 || values.length() > 2 )
6037 return QVariant();
6038
6039 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6040 int prec = 8;
6041 if ( values.length() == 2 )
6042 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6043 QString wkt = fGeom.asWkt( prec );
6044 return QVariant( wkt );
6045}
6046
6047static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6048{
6049 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6050 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
6051}
6052
6053static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6054{
6055 if ( values.length() != 2 )
6056 {
6057 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
6058 return QVariant();
6059 }
6060
6061 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6062 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6063
6064 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6065 if ( !pt1 && fGeom1.isMultipart() )
6066 {
6068 {
6069 if ( collection->numGeometries() == 1 )
6070 {
6071 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6072 }
6073 }
6074 }
6075
6076 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6077 if ( !pt2 && fGeom2.isMultipart() )
6078 {
6080 {
6081 if ( collection->numGeometries() == 1 )
6082 {
6083 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6084 }
6085 }
6086 }
6087
6088 if ( !pt1 || !pt2 )
6089 {
6090 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
6091 return QVariant();
6092 }
6093
6094 // Code from PostGIS
6095 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
6096 {
6097 if ( pt1->y() < pt2->y() )
6098 return 0.0;
6099 else if ( pt1->y() > pt2->y() )
6100 return M_PI;
6101 else
6102 return 0;
6103 }
6104
6105 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
6106 {
6107 if ( pt1->x() < pt2->x() )
6108 return M_PI_2;
6109 else if ( pt1->x() > pt2->x() )
6110 return M_PI + ( M_PI_2 );
6111 else
6112 return 0;
6113 }
6114
6115 if ( pt1->x() < pt2->x() )
6116 {
6117 if ( pt1->y() < pt2->y() )
6118 {
6119 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
6120 }
6121 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
6122 {
6123 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI_2 );
6124 }
6125 }
6126
6127 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
6128 {
6129 if ( pt1->y() > pt2->y() )
6130 {
6131 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI;
6132 }
6133 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
6134 {
6135 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI_2 ) );
6136 }
6137 }
6138}
6139
6140static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6141{
6142 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6143 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6144 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6145 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
6146
6147 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
6148 {
6149 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
6150 return QVariant();
6151 }
6152
6153 const QgsPointXY point1 = geom1.asPoint();
6154 const QgsPointXY point2 = geom2.asPoint();
6155 if ( point1.isEmpty() || point2.isEmpty() )
6156 {
6157 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
6158 return QVariant();
6159 }
6160
6162 if ( context )
6163 {
6164 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6165
6166 if ( !sourceCrs.isValid() )
6167 {
6168 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
6169 }
6170
6171 if ( ellipsoid.isEmpty() )
6172 {
6173 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
6174 }
6175 }
6176
6177 if ( !sourceCrs.isValid() )
6178 {
6179 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
6180 return QVariant();
6181 }
6182
6183 QgsDistanceArea da;
6184 da.setSourceCrs( sourceCrs, tContext );
6185 if ( !da.setEllipsoid( ellipsoid ) )
6186 {
6187 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
6188 return QVariant();
6189 }
6190
6191 try
6192 {
6193 const double bearing = da.bearing( point1, point2 );
6194 if ( std::isfinite( bearing ) )
6195 {
6196 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
6197 }
6198 }
6199 catch ( QgsCsException &cse )
6200 {
6201 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
6202 return QVariant();
6203 }
6204 return QVariant();
6205}
6206
6207static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6208{
6209 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6210
6212 {
6213 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
6214 return QVariant();
6215 }
6216
6217 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6218 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6219 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
6220
6221 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef() );
6222 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
6223
6224 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
6225}
6226
6227static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6228{
6229 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6230 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6231
6232 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6233 if ( !pt1 && fGeom1.isMultipart() )
6234 {
6236 {
6237 if ( collection->numGeometries() == 1 )
6238 {
6239 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6240 }
6241 }
6242 }
6243 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6244 if ( !pt2 && fGeom2.isMultipart() )
6245 {
6247 {
6248 if ( collection->numGeometries() == 1 )
6249 {
6250 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6251 }
6252 }
6253 }
6254
6255 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) || !pt1 || !pt2 )
6256 {
6257 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6258 return QVariant();
6259 }
6260
6261 return pt1->inclination( *pt2 );
6262}
6263
6264static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6265{
6266 if ( values.length() != 3 )
6267 return QVariant();
6268
6269 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6270 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6271 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6272
6273 QgsGeometry geom = fGeom.extrude( x, y );
6274
6275 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6276 return result;
6277}
6278
6279static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6280{
6281 if ( values.length() < 2 )
6282 return QVariant();
6283
6284 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6285
6286 if ( !fGeom.isMultipart() )
6287 return values.at( 0 );
6288
6289 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6290 QVariant cachedExpression;
6291 if ( ctx )
6292 cachedExpression = ctx->cachedValue( expString );
6293 QgsExpression expression;
6294
6295 if ( cachedExpression.isValid() )
6296 {
6297 expression = cachedExpression.value<QgsExpression>();
6298 }
6299 else
6300 expression = QgsExpression( expString );
6301
6302 bool asc = values.value( 2 ).toBool();
6303
6304 QgsExpressionContext *unconstedContext = nullptr;
6305 QgsFeature f;
6306 if ( ctx )
6307 {
6308 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6309 // so no reason to worry
6310 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6311 f = ctx->feature();
6312 }
6313 else
6314 {
6315 // If there's no context provided, create a fake one
6316 unconstedContext = new QgsExpressionContext();
6317 }
6318
6320 Q_ASSERT( collection ); // Should have failed the multipart check above
6321
6323 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6324 QgsExpressionSorter sorter( orderBy );
6325
6326 QList<QgsFeature> partFeatures;
6327 partFeatures.reserve( collection->partCount() );
6328 for ( int i = 0; i < collection->partCount(); ++i )
6329 {
6330 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6331 partFeatures << f;
6332 }
6333
6334 sorter.sortFeatures( partFeatures, unconstedContext );
6335
6337
6338 Q_ASSERT( orderedGeom );
6339
6340 while ( orderedGeom->partCount() )
6341 orderedGeom->removeGeometry( 0 );
6342
6343 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6344 {
6345 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6346 }
6347
6348 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6349
6350 if ( !ctx )
6351 delete unconstedContext;
6352
6353 return result;
6354}
6355
6356static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6357{
6358 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6359 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6360
6361 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6362
6363 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6364 return result;
6365}
6366
6367static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6368{
6369 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6370 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6371
6372 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6373
6374 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6375 return result;
6376}
6377
6378static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6379{
6380 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6381 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6382
6383 QgsGeometry geom = lineGeom.interpolate( distance );
6384
6385 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6386 return result;
6387}
6388
6389static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6390{
6391 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6392 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6393 const bool use3DDistance = values.at( 2 ).toBool();
6394
6395 double x, y, z, distance;
6396
6398 if ( !line )
6399 {
6400 return QVariant();
6401 }
6402
6403 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6404 {
6405 QgsPoint point( x, y );
6406 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6407 {
6408 point.addZValue( z );
6409 }
6410 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6411 }
6412
6413 return QVariant();
6414}
6415
6416static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6417{
6418 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6419 if ( lineGeom.type() != Qgis::GeometryType::Line )
6420 {
6421 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6422 return QVariant();
6423 }
6424
6425 const QgsCurve *curve = nullptr;
6426 if ( !lineGeom.isMultipart() )
6427 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6428 else
6429 {
6431 {
6432 if ( collection->numGeometries() > 0 )
6433 {
6434 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6435 }
6436 }
6437 }
6438 if ( !curve )
6439 return QVariant();
6440
6441 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6442 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6443
6444 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6445 QgsGeometry result( std::move( substring ) );
6446 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6447}
6448
6449static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6450{
6451 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6452 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6453
6454 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6455}
6456
6457static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6458{
6459 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6460 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6461 if ( vertex < 0 )
6462 {
6463 //negative idx
6464 int count = geom.constGet()->nCoordinates();
6465 vertex = count + vertex;
6466 }
6467
6468 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6469}
6470
6471static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6472{
6473 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6474 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6475 if ( vertex < 0 )
6476 {
6477 //negative idx
6478 int count = geom.constGet()->nCoordinates();
6479 vertex = count + vertex;
6480 }
6481
6482 return geom.distanceToVertex( vertex );
6483}
6484
6485static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6486{
6487 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6488 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6489
6490 double distance = lineGeom.lineLocatePoint( pointGeom );
6491
6492 return distance >= 0 ? distance : QVariant();
6493}
6494
6495static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6496{
6497 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6498 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6499 const bool use3DDistance = values.at( 2 ).toBool();
6500
6501 double x, y, z, distance;
6502
6504 if ( !line )
6505 {
6506 return QVariant();
6507 }
6508
6509 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6510 return found ? distance : QVariant();
6511}
6512
6513static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6514{
6515 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6516 {
6517 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6518 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6519 }
6520
6521 if ( values.length() >= 1 )
6522 {
6523 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6524 return QVariant( qlonglong( std::round( number ) ) );
6525 }
6526
6527 return QVariant();
6528}
6529
6530static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6531{
6532 Q_UNUSED( values )
6533 Q_UNUSED( parent )
6534 return M_PI;
6535}
6536
6537static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6538{
6539 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6540 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6541 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6542 if ( places < 0 )
6543 {
6544 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6545 return QVariant();
6546 }
6547
6548 const bool omitGroupSeparator = values.value( 3 ).toBool();
6549 const bool trimTrailingZeros = values.value( 4 ).toBool();
6550
6551 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6552 if ( !omitGroupSeparator )
6553 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6554 else
6555 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6556
6557 QString res = locale.toString( value, 'f', places );
6558
6559 if ( trimTrailingZeros )
6560 {
6561 const QChar decimal = locale.decimalPoint().at( 0 );
6562 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6563
6564 if ( res.contains( decimal ) )
6565 {
6566 int trimPoint = res.length() - 1;
6567
6568 while ( res.at( trimPoint ) == zeroDigit )
6569 trimPoint--;
6570
6571 if ( res.at( trimPoint ) == decimal )
6572 trimPoint--;
6573
6574 res.truncate( trimPoint + 1 );
6575 }
6576 }
6577
6578 return res;
6579}
6580
6581static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6582{
6583 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6584 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6585 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6586
6587 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6588 if ( format.indexOf( "Z" ) > 0 )
6589 datetime = datetime.toUTC();
6590
6591 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6592 return locale.toString( datetime, format );
6593}
6594
6595static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6596{
6597 const QVariant variant = values.at( 0 );
6598 bool isQColor;
6599 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6600 if ( !color.isValid() )
6601 return QVariant();
6602
6603 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6604 if ( color.spec() == QColor::Spec::Cmyk )
6605 {
6606 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() )
6607 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6608 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6609 }
6610 else
6611 {
6612 const float avg = ( color.redF() + color.greenF() + color.blueF() )
6613 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6614 color.setRgbF( avg, avg, avg, alpha );
6615 }
6616
6617 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6618}
6619
6620static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6621{
6622 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6623 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6624 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6625 if ( ratio > 1 )
6626 {
6627 ratio = 1;
6628 }
6629 else if ( ratio < 0 )
6630 {
6631 ratio = 0;
6632 }
6633
6634 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6635 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6636 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6637 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6638
6639 QColor newColor( red, green, blue, alpha );
6640
6641 return QgsSymbolLayerUtils::encodeColor( newColor );
6642}
6643
6644static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6645{
6646 const QVariant variant1 = values.at( 0 );
6647 const QVariant variant2 = values.at( 1 );
6648
6649 if ( variant1.userType() != variant2.userType() )
6650 {
6651 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6652 return QVariant();
6653 }
6654
6655 bool isQColor;
6656 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6657 if ( !color1.isValid() )
6658 return QVariant();
6659
6660 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6661 if ( !color2.isValid() )
6662 return QVariant();
6663
6664 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6665 {
6666 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6667 return QVariant();
6668 }
6669
6670 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6671
6672 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6673 // NOLINTBEGIN(bugprone-narrowing-conversions)
6674
6675 QColor newColor;
6676 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6677 if ( color1.spec() == QColor::Spec::Cmyk )
6678 {
6679 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6680 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6681 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6682 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6683 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6684 }
6685 else
6686 {
6687 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6688 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6689 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6690 newColor = QColor::fromRgbF( red, green, blue, alpha );
6691 }
6692
6693 // NOLINTEND(bugprone-narrowing-conversions)
6694
6695 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6696}
6697
6698static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6699{
6700 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6701 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6702 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6703 QColor color = QColor( red, green, blue );
6704 if ( !color.isValid() )
6705 {
6706 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6707 color = QColor( 0, 0, 0 );
6708 }
6709
6710 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6711}
6712
6713static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6714{
6715 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6716 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6717 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6718 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6719 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6720 if ( !color.isValid() )
6721 {
6722 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6723 return QVariant();
6724 }
6725
6726 return color;
6727}
6728
6729static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6730{
6731 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6732 QVariant value = node->eval( parent, context );
6733 if ( parent->hasEvalError() )
6734 {
6735 parent->setEvalErrorString( QString() );
6736 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6738 value = node->eval( parent, context );
6740 }
6741 return value;
6742}
6743
6744static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6745{
6746 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6748 QVariant value = node->eval( parent, context );
6750 if ( value.toBool() )
6751 {
6752 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6754 value = node->eval( parent, context );
6756 }
6757 else
6758 {
6759 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6761 value = node->eval( parent, context );
6763 }
6764 return value;
6765}
6766
6767static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6768{
6769 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6770 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6771 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6772 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6773 QColor color = QColor( red, green, blue, alpha );
6774 if ( !color.isValid() )
6775 {
6776 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6777 color = QColor( 0, 0, 0 );
6778 }
6779 return QgsSymbolLayerUtils::encodeColor( color );
6780}
6781
6782QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6783{
6784 QgsGradientColorRamp expRamp;
6785 const QgsColorRamp *ramp = nullptr;
6786 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6787 {
6788 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6789 ramp = &expRamp;
6790 }
6791 else
6792 {
6793 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6794 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6795 if ( !ramp )
6797 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6798 return QVariant();
6799 }
6800 }
6801
6802 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6803 QColor color = ramp->color( value );
6804 return color;
6805}
6806
6807QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6808{
6809 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6810 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6811}
6812
6813static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6814{
6815 // Hue ranges from 0 - 360
6816 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6817 // Saturation ranges from 0 - 100
6818 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6819 // Lightness ranges from 0 - 100
6820 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6821
6822 QColor color = QColor::fromHslF( hue, saturation, lightness );
6823
6824 if ( !color.isValid() )
6825 {
6826 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6827 color = QColor( 0, 0, 0 );
6828 }
6829
6830 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6831}
6832
6833static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6834{
6835 // Hue ranges from 0 - 360
6836 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6837 // Saturation ranges from 0 - 100
6838 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6839 // Lightness ranges from 0 - 100
6840 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6841 // Alpha ranges from 0 - 255
6842 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6843
6844 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6845 if ( !color.isValid() )
6846 {
6847 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6848 color = QColor( 0, 0, 0 );
6849 }
6850 return QgsSymbolLayerUtils::encodeColor( color );
6851}
6852
6853static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6854{
6855 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6856 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6857 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6858 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6859
6860 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6861 if ( !color.isValid() )
6862 {
6863 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6864 return QVariant();
6865 }
6866
6867 return color;
6868}
6869
6870static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6871{
6872 // Hue ranges from 0 - 360
6873 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6874 // Saturation ranges from 0 - 100
6875 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6876 // Value ranges from 0 - 100
6877 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6878
6879 QColor color = QColor::fromHsvF( hue, saturation, value );
6880
6881 if ( !color.isValid() )
6882 {
6883 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6884 color = QColor( 0, 0, 0 );
6885 }
6886
6887 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6888}
6889
6890static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6891{
6892 // Hue ranges from 0 - 360
6893 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6894 // Saturation ranges from 0 - 100
6895 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6896 // Value ranges from 0 - 100
6897 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6898 // Alpha ranges from 0 - 255
6899 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6900
6901 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6902 if ( !color.isValid() )
6903 {
6904 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6905 color = QColor( 0, 0, 0 );
6906 }
6907 return QgsSymbolLayerUtils::encodeColor( color );
6908}
6909
6910static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6911{
6912 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6913 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6914 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6915 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6916 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6917
6918 if ( !color.isValid() )
6919 {
6920 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6921 return QVariant();
6922 }
6923
6924 return color;
6925}
6926
6927static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6928{
6929 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6930 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6931 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6932 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6933 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6934
6935 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6936 if ( !color.isValid() )
6937 {
6938 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6939 return QVariant();
6940 }
6941
6942 return color;
6943}
6944
6945static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6946{
6947 // Cyan ranges from 0 - 100
6948 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6949 // Magenta ranges from 0 - 100
6950 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6951 // Yellow ranges from 0 - 100
6952 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6953 // Black ranges from 0 - 100
6954 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6955
6956 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6957
6958 if ( !color.isValid() )
6959 {
6960 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6961 color = QColor( 0, 0, 0 );
6962 }
6963
6964 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6965}
6966
6967static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6968{
6969 // Cyan ranges from 0 - 100
6970 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6971 // Magenta ranges from 0 - 100
6972 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6973 // Yellow ranges from 0 - 100
6974 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6975 // Black ranges from 0 - 100
6976 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6977 // Alpha ranges from 0 - 255
6978 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6979
6980 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6981 if ( !color.isValid() )
6982 {
6983 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6984 color = QColor( 0, 0, 0 );
6985 }
6986 return QgsSymbolLayerUtils::encodeColor( color );
6987}
6988
6989static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6990{
6991 const QVariant variant = values.at( 0 );
6992 bool isQColor;
6993 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6994 if ( !color.isValid() )
6995 return QVariant();
6996
6997 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6998 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6999 return color.red();
7000 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
7001 return color.green();
7002 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
7003 return color.blue();
7004 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
7005 return color.alpha();
7006 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7007 return static_cast< double >( color.hsvHueF() * 360 );
7008 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7009 return static_cast< double >( color.hsvSaturationF() * 100 );
7010 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7011 return static_cast< double >( color.valueF() * 100 );
7012 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7013 return static_cast< double >( color.hslHueF() * 360 );
7014 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7015 return static_cast< double >( color.hslSaturationF() * 100 );
7016 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7017 return static_cast< double >( color.lightnessF() * 100 );
7018 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7019 return static_cast< double >( color.cyanF() * 100 );
7020 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7021 return static_cast< double >( color.magentaF() * 100 );
7022 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7023 return static_cast< double >( color.yellowF() * 100 );
7024 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7025 return static_cast< double >( color.blackF() * 100 );
7026
7027 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7028 return QVariant();
7029}
7030
7031static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7032{
7033 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7034 if ( map.empty() )
7035 {
7036 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
7037 return QVariant();
7038 }
7039
7040 QList< QColor > colors;
7042 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7043 {
7044 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
7045 if ( !colors.last().isValid() )
7046 {
7047 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
7048 return QVariant();
7049 }
7050
7051 double step = it.key().toDouble();
7052 if ( it == map.constBegin() )
7053 {
7054 if ( step != 0.0 )
7055 stops << QgsGradientStop( step, colors.last() );
7056 }
7057 else if ( it == map.constEnd() )
7058 {
7059 if ( step != 1.0 )
7060 stops << QgsGradientStop( step, colors.last() );
7061 }
7062 else
7063 {
7064 stops << QgsGradientStop( step, colors.last() );
7065 }
7066 }
7067 bool discrete = values.at( 1 ).toBool();
7068
7069 if ( colors.empty() )
7070 return QVariant();
7071
7072 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
7073}
7074
7075static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7076{
7077 const QVariant variant = values.at( 0 );
7078 bool isQColor;
7079 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7080 if ( !color.isValid() )
7081 return QVariant();
7082
7083 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7084 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7085 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
7086 color.setRed( std::clamp( value, 0, 255 ) );
7087 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
7088 color.setGreen( std::clamp( value, 0, 255 ) );
7089 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
7090 color.setBlue( std::clamp( value, 0, 255 ) );
7091 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
7092 color.setAlpha( std::clamp( value, 0, 255 ) );
7093 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7094 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
7095 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7096 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
7097 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7098 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7099 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7100 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
7101 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7102 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
7103 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7104 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7105 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7106 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
7107 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7108 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
7109 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7110 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
7111 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7112 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7113 else
7114 {
7115 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7116 return QVariant();
7117 }
7118 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7119}
7120
7121static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7122{
7123 const QVariant variant = values.at( 0 );
7124 bool isQColor;
7125 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7126 if ( !color.isValid() )
7127 return QVariant();
7128
7129 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7130
7131 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7132}
7133
7134static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7135{
7136 const QVariant variant = values.at( 0 );
7137 bool isQColor;
7138 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7139 if ( !color.isValid() )
7140 return QVariant();
7141
7142 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7143
7144 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7145}
7146
7147static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7148{
7149 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7150 QgsGeometry geom = feat.geometry();
7151 if ( !geom.isNull() )
7152 return QVariant::fromValue( geom );
7153 return QVariant();
7154}
7155
7156static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7157{
7158 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7159 if ( !feat.isValid() )
7160 return QVariant();
7161 return feat.id();
7162}
7163
7164static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7165{
7166 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
7167 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
7168 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
7169
7170 if ( !sCrs.isValid() )
7171 return QVariant::fromValue( fGeom );
7172
7173 if ( !dCrs.isValid() )
7174 return QVariant::fromValue( fGeom );
7175
7177 if ( context )
7178 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
7179 QgsCoordinateTransform t( sCrs, dCrs, tContext );
7180 try
7181 {
7183 return QVariant::fromValue( fGeom );
7184 }
7185 catch ( QgsCsException &cse )
7186 {
7187 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
7188 return QVariant();
7189 }
7190 return QVariant();
7191}
7192
7193
7194static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7195{
7196 bool foundLayer = false;
7197 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7198
7199 //no layer found
7200 if ( !featureSource || !foundLayer )
7201 {
7202 return QVariant();
7203 }
7204
7205 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
7206
7208 req.setFilterFid( fid );
7209 req.setTimeout( 10000 );
7210 req.setRequestMayBeNested( true );
7211 if ( context )
7212 req.setFeedback( context->feedback() );
7213 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7214
7215 QgsFeature fet;
7216 QVariant result;
7217 if ( fIt.nextFeature( fet ) )
7218 result = QVariant::fromValue( fet );
7219
7220 return result;
7221}
7222
7223static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7224{
7225 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
7226 bool foundLayer = false;
7227 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7228
7229 //no layer found
7230 if ( !featureSource || !foundLayer )
7231 {
7232 return QVariant();
7233 }
7235 QString cacheValueKey;
7236 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7237 {
7238 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7239
7240 QMap<QString, QVariant>::const_iterator i = attributeMap.constBegin();
7241 QString filterString;
7242 for ( ; i != attributeMap.constEnd(); ++i )
7243 {
7244 if ( !filterString.isEmpty() )
7245 {
7246 filterString.append( " AND " );
7247 }
7248 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7249 }
7250 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7251 if ( context && context->hasCachedValue( cacheValueKey ) )
7252 {
7253 return context->cachedValue( cacheValueKey );
7254 }
7255 req.setFilterExpression( filterString );
7256 }
7257 else
7258 {
7259 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7260 int attributeId = featureSource->fields().lookupField( attribute );
7261 if ( attributeId == -1 )
7262 {
7263 return QVariant();
7264 }
7265
7266 const QVariant &attVal = values.at( 2 );
7267
7268 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7269 if ( context && context->hasCachedValue( cacheValueKey ) )
7270 {
7271 return context->cachedValue( cacheValueKey );
7272 }
7273
7275 }
7276 req.setLimit( 1 );
7277 req.setTimeout( 10000 );
7278 req.setRequestMayBeNested( true );
7279 if ( context )
7280 req.setFeedback( context->feedback() );
7281 if ( !parent->needsGeometry() )
7282 {
7284 }
7285 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7286
7287 QgsFeature fet;
7288 QVariant res;
7289 if ( fIt.nextFeature( fet ) )
7290 {
7291 res = QVariant::fromValue( fet );
7292 }
7293
7294 if ( context )
7295 context->setCachedValue( cacheValueKey, res );
7296 return res;
7297}
7298
7299static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7300{
7301 QVariant result;
7302 QString fieldName;
7303
7304 if ( context )
7305 {
7306 if ( !values.isEmpty() )
7307 {
7308 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7309 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7310 fieldName = col->name();
7311 else if ( values.size() == 2 )
7312 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7313 }
7314
7315 QVariant value = values.at( 0 );
7316
7317 const QgsFields fields = context->fields();
7318 int fieldIndex = fields.lookupField( fieldName );
7319
7320 if ( fieldIndex == -1 )
7321 {
7322 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7323 }
7324 else
7325 {
7326 // TODO this function is NOT thread safe
7328 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7330
7331 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7332 if ( context->hasCachedValue( cacheValueKey ) )
7333 {
7334 return context->cachedValue( cacheValueKey );
7335 }
7336
7337 if ( layer )
7338 {
7339 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7341
7342 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7343
7344 QVariant cache;
7345 if ( !context->hasCachedValue( cacheKey ) )
7346 {
7347 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7348 context->setCachedValue( cacheKey, cache );
7349 }
7350 else
7351 cache = context->cachedValue( cacheKey );
7352
7353 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7354 }
7355
7356 context->setCachedValue( cacheValueKey, result );
7357 }
7358 }
7359 else
7360 {
7361 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7362 }
7363
7364 return result;
7365}
7366
7367static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7368{
7369 const QVariant data = values.at( 0 );
7370 const QMimeDatabase db;
7371 return db.mimeTypeForData( data.toByteArray() ).name();
7372}
7373
7374static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7375{
7376 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7377
7378 bool translate = true;
7379 if ( values.length() >= 3 )
7380 {
7381 translate = QgsExpressionUtils::getTVLValue( values.at( 2 ), parent ) == QgsExpressionUtils::TVL::True;
7382 }
7383
7384 bool foundLayer = false;
7385 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7386 values.at( 0 ),
7387 context,
7388 parent,
7389 [layerProperty, translate]( QgsMapLayer *layer ) -> QVariant {
7390 if ( !layer )
7391 return QVariant();
7392
7393 // here, we always prefer the layer metadata values over the older server-specific published values
7394 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7395 return layer->name();
7396 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7397 return layer->id();
7398 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7399 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7400 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7401 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7402 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7403 {
7404 QStringList keywords;
7405 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7406 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7407 {
7408 keywords.append( it.value() );
7409 }
7410 if ( !keywords.isEmpty() )
7411 return keywords;
7412 return layer->serverProperties()->keywordList();
7413 }
7414 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7415 return layer->serverProperties()->dataUrl();
7416 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7417 {
7418 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7419 }
7420 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7421 return layer->serverProperties()->attributionUrl();
7422 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7423 return layer->publicSource();
7424 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7425 return layer->minimumScale();
7426 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7427 return layer->maximumScale();
7428 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7429 return layer->isEditable();
7430 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7431 return layer->crs().authid();
7432 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7433 return layer->crs().toProj();
7434 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7435 return layer->crs().description();
7436 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7437 return layer->crs().ellipsoidAcronym();
7438 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7439 {
7440 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7441 QVariant result = QVariant::fromValue( extentGeom );
7442 return result;
7443 }
7444 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7445 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7446 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7447 {
7448 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7449 return decodedUri.value( u"path"_s );
7450 }
7451 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7452 {
7453 switch ( layer->type() )
7454 {
7456 return translate ? QCoreApplication::translate( "expressions", "Vector" ) : u"Vector"_s;
7458 return translate ? QCoreApplication::translate( "expressions", "Raster" ) : u"Raster"_s;
7460 return translate ? QCoreApplication::translate( "expressions", "Mesh" ) : u"Mesh"_s;
7462 return translate ? QCoreApplication::translate( "expressions", "Vector Tile" ) : u"Vector Tile"_s;
7464 return translate ? QCoreApplication::translate( "expressions", "Plugin" ) : u"Plugin"_s;
7466 return translate ? QCoreApplication::translate( "expressions", "Annotation" ) : u"Annotation"_s;
7468 return translate ? QCoreApplication::translate( "expressions", "Point Cloud" ) : u"Point Cloud"_s;
7470 return translate ? QCoreApplication::translate( "expressions", "Group" ) : u"Group"_s;
7472 return translate ? QCoreApplication::translate( "expressions", "Tiled Scene" ) : u"Tiled Scene"_s;
7473 }
7474 }
7475 else
7476 {
7477 //vector layer methods
7478 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7479 if ( vLayer )
7480 {
7481 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7482 return vLayer->storageType();
7483 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7485 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7486 return QVariant::fromValue( vLayer->featureCount() );
7487 }
7488 }
7489
7490 return QVariant();
7491 },
7492 foundLayer
7493 );
7494
7495 if ( !foundLayer )
7496 return QVariant();
7497 else
7498 return res;
7499}
7500
7501static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7502{
7503 const QString uriPart = values.at( 1 ).toString();
7504
7505 bool foundLayer = false;
7506
7507 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7508 values.at( 0 ),
7509 context,
7510 parent,
7511 [parent, uriPart]( QgsMapLayer *layer ) -> QVariant {
7512 if ( !layer->dataProvider() )
7513 {
7514 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7515 return QVariant();
7516 }
7517
7518 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7519
7520 if ( !uriPart.isNull() )
7521 {
7522 return decodedUri.value( uriPart );
7523 }
7524 else
7525 {
7526 return decodedUri;
7527 }
7528 },
7529 foundLayer
7530 );
7531
7532 if ( !foundLayer )
7533 {
7534 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7535 return QVariant();
7536 }
7537 else
7538 {
7539 return res;
7540 }
7541}
7542
7543static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7544{
7545 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7546 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7547
7548 bool foundLayer = false;
7549 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7550 values.at( 0 ),
7551 context,
7552 parent,
7553 [parent, band, layerProperty]( QgsMapLayer *layer ) -> QVariant {
7554 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7555 if ( !rl )
7556 return QVariant();
7557
7558 if ( band < 1 || band > rl->bandCount() )
7559 {
7560 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7561 return QVariant();
7562 }
7563
7565
7566 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7568 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7570 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7572 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7574 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7576 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7578 else
7579 {
7580 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7581 return QVariant();
7582 }
7583
7584 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7585 switch ( stat )
7586 {
7588 return stats.mean;
7590 return stats.stdDev;
7592 return stats.minimumValue;
7594 return stats.maximumValue;
7596 return stats.range;
7598 return stats.sum;
7599 default:
7600 break;
7601 }
7602 return QVariant();
7603 },
7604 foundLayer
7605 );
7606
7607 if ( !foundLayer )
7608 {
7609#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7610 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7611#endif
7612 return QVariant();
7613 }
7614 else
7615 {
7616 return res;
7617 }
7618}
7619
7620static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7621{
7622 return values;
7623}
7624
7625static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7626{
7627 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7628 bool ascending = values.value( 1 ).toBool();
7629 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7630 return list;
7631}
7632
7633static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7634{
7635 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7636}
7637
7638static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7639{
7640 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7641}
7642
7643static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7644{
7645 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7646}
7647
7648static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7649{
7650 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7651 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7652 int match = 0;
7653 for ( const auto &item : listB )
7654 {
7655 if ( listA.contains( item ) )
7656 match++;
7657 }
7658
7659 return QVariant( match == listB.count() );
7660}
7661
7662static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7663{
7664 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7665}
7666
7667static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7668{
7669 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7670 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7671 if ( pos < list.length() && pos >= 0 )
7672 return list.at( pos );
7673 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7674 return list.at( list.length() + pos );
7675 return QVariant();
7676}
7677
7678static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7679{
7680 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7681 return list.value( 0 );
7682}
7683
7684static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7685{
7686 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7687 return list.value( list.size() - 1 );
7688}
7689
7690static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7691{
7692 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7693 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7694}
7695
7696static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7697{
7698 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7699 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7700}
7701
7702static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7703{
7704 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7705 int i = 0;
7706 double total = 0.0;
7707 for ( const QVariant &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 total += item.toDouble();
7718 ++i;
7719 break;
7720 }
7721 }
7722 return i == 0 ? QVariant() : total / i;
7723}
7724
7725static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7726{
7727 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7728 QVariantList numbers;
7729 for ( const auto &item : list )
7730 {
7731 switch ( item.userType() )
7732 {
7733 case QMetaType::Int:
7734 case QMetaType::UInt:
7735 case QMetaType::LongLong:
7736 case QMetaType::ULongLong:
7737 case QMetaType::Float:
7738 case QMetaType::Double:
7739 numbers.append( item );
7740 break;
7741 }
7742 }
7743 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7744 const int count = numbers.count();
7745 if ( count == 0 )
7746 {
7747 return QVariant();
7748 }
7749 else if ( count % 2 )
7750 {
7751 return numbers.at( count / 2 );
7752 }
7753 else
7754 {
7755 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7756 }
7757}
7758
7759static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7760{
7761 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7762 int i = 0;
7763 double total = 0.0;
7764 for ( const QVariant &item : list )
7765 {
7766 switch ( item.userType() )
7767 {
7768 case QMetaType::Int:
7769 case QMetaType::UInt:
7770 case QMetaType::LongLong:
7771 case QMetaType::ULongLong:
7772 case QMetaType::Float:
7773 case QMetaType::Double:
7774 total += item.toDouble();
7775 ++i;
7776 break;
7777 }
7778 }
7779 return i == 0 ? QVariant() : total;
7780}
7781
7782static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7783{
7784 QVariant result = value;
7785 ( void ) result.convert( static_cast<int>( type ) );
7786 return result;
7787}
7788
7789static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7790{
7791 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7792 QHash< QVariant, int > hash;
7793 for ( const auto &item : list )
7794 {
7795 ++hash[item];
7796 }
7797 const QList< int > occurrences = hash.values();
7798 if ( occurrences.empty() )
7799 return QVariantList();
7800
7801 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7802
7803 const QString option = values.at( 1 ).toString();
7804 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7805 {
7806 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7807 }
7808 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7809 {
7810 if ( hash.isEmpty() )
7811 return QVariant();
7812
7813 return QVariant( hash.key( maxValue ) );
7814 }
7815 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7816 {
7817 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7818 }
7819 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7820 {
7821 if ( maxValue * 2 <= list.size() )
7822 return QVariant();
7823
7824 return QVariant( hash.key( maxValue ) );
7825 }
7826 else
7827 {
7828 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7829 return QVariant();
7830 }
7831}
7832
7833static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7834{
7835 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7836 QHash< QVariant, int > hash;
7837 for ( const auto &item : list )
7838 {
7839 ++hash[item];
7840 }
7841 const QList< int > occurrences = hash.values();
7842 if ( occurrences.empty() )
7843 return QVariantList();
7844
7845 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7846
7847 const QString option = values.at( 1 ).toString();
7848 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7849 {
7850 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7851 }
7852 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7853 {
7854 if ( hash.isEmpty() )
7855 return QVariant();
7856
7857 return QVariant( hash.key( minValue ) );
7858 }
7859 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7860 {
7861 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7862 }
7863 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7864 {
7865 if ( hash.isEmpty() )
7866 return QVariant();
7867
7868 // Remove the majority, all others are minority
7869 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7870 if ( maxValue * 2 > list.size() )
7871 hash.remove( hash.key( maxValue ) );
7872
7873 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7874 }
7875 else
7876 {
7877 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7878 return QVariant();
7879 }
7880}
7881
7882static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7883{
7884 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7885 list.append( values.at( 1 ) );
7886 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7887}
7888
7889static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7890{
7891 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7892 list.prepend( values.at( 1 ) );
7893 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7894}
7895
7896static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7897{
7898 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7899 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7900 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7901}
7902
7903static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7904{
7905 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7906 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7907 if ( position < 0 )
7908 position = position + list.length();
7909 if ( position >= 0 && position < list.length() )
7910 list.removeAt( position );
7911 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7912}
7913
7914static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7915{
7916 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7917 return QVariant();
7918
7919 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7920
7921 const QVariant toRemove = values.at( 1 );
7922 if ( QgsVariantUtils::isNull( toRemove ) )
7923 {
7924 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant &element ) { return QgsVariantUtils::isNull( element ); } ), list.end() );
7925 }
7926 else
7927 {
7928 list.removeAll( toRemove );
7929 }
7930 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7931}
7932
7933static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7934{
7935 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7936 {
7937 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7938
7939 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7940 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7941 {
7942 int index = list.indexOf( it.key() );
7943 while ( index >= 0 )
7944 {
7945 list.replace( index, it.value() );
7946 index = list.indexOf( it.key() );
7947 }
7948 }
7949
7950 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7951 }
7952 else if ( values.count() == 3 )
7953 {
7954 QVariantList before;
7955 QVariantList after;
7956 bool isSingleReplacement = false;
7957
7958 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7959 {
7960 before = QVariantList() << values.at( 1 );
7961 }
7962 else
7963 {
7964 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7965 }
7966
7967 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7968 {
7969 after = QVariantList() << values.at( 2 );
7970 isSingleReplacement = true;
7971 }
7972 else
7973 {
7974 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7975 }
7976
7977 if ( !isSingleReplacement && before.length() != after.length() )
7978 {
7979 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7980 return QVariant();
7981 }
7982
7983 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7984 for ( int i = 0; i < before.length(); i++ )
7985 {
7986 int index = list.indexOf( before.at( i ) );
7987 while ( index >= 0 )
7988 {
7989 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7990 index = list.indexOf( before.at( i ) );
7991 }
7992 }
7993
7994 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7995 }
7996 else
7997 {
7998 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7999 return QVariant();
8000 }
8001}
8002
8003static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8004{
8005 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8006 QVariantList list_new;
8007
8008 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
8009 {
8010 while ( list.removeOne( cur ) )
8011 {
8012 list_new.append( cur );
8013 }
8014 }
8015
8016 list_new.append( list );
8017
8018 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
8019}
8020
8021static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8022{
8023 QVariantList list;
8024 for ( const QVariant &cur : values )
8025 {
8026 list += QgsExpressionUtils::getListValue( cur, parent );
8027 }
8028 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
8029}
8030
8031static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8032{
8033 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8034 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
8035 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
8036 int slice_length = 0;
8037 // negative positions means positions taken relative to the end of the array
8038 if ( start_pos < 0 )
8039 {
8040 start_pos = list.length() + start_pos;
8041 }
8042 if ( end_pos >= 0 )
8043 {
8044 slice_length = end_pos - start_pos + 1;
8045 }
8046 else
8047 {
8048 slice_length = list.length() + end_pos - start_pos + 1;
8049 }
8050 //avoid negative lengths in QList.mid function
8051 if ( slice_length < 0 )
8052 {
8053 slice_length = 0;
8054 }
8055 list = list.mid( start_pos, slice_length );
8056 return list;
8057}
8058
8059static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8060{
8061 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8062 std::reverse( list.begin(), list.end() );
8063 return list;
8064}
8065
8066static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8067{
8068 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8069 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
8070 for ( const QVariant &cur : array2 )
8071 {
8072 if ( array1.contains( cur ) )
8073 return QVariant( true );
8074 }
8075 return QVariant( false );
8076}
8077
8078static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8079{
8080 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8081
8082 QVariantList distinct;
8083
8084 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8085 {
8086 if ( !distinct.contains( *it ) )
8087 {
8088 distinct += ( *it );
8089 }
8090 }
8091
8092 return distinct;
8093}
8094
8095static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8096{
8097 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8098 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8099 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8100
8101 QString str;
8102
8103 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8104 {
8105 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
8106 if ( it != ( array.constEnd() - 1 ) )
8107 {
8108 str += delimiter;
8109 }
8110 }
8111
8112 return QVariant( str );
8113}
8114
8115static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8116{
8117 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8118 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8119 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8120
8121 QStringList list = str.split( delimiter );
8122 QVariantList array;
8123
8124 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
8125 {
8126 array += ( !( *it ).isEmpty() ) ? *it : empty;
8127 }
8128
8129 return array;
8130}
8131
8132static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8133{
8134 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8135 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
8136 if ( document.isNull() )
8137 return QVariant();
8138
8139 return document.toVariant();
8140}
8141
8142static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8143{
8144 Q_UNUSED( parent )
8145 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
8146 return QString( document.toJson( QJsonDocument::Compact ) );
8147}
8148
8149static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8150{
8151 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8152 if ( str.isEmpty() )
8153 return QVariantMap();
8154 str = str.trimmed();
8155
8156 return QgsHstoreUtils::parse( str );
8157}
8158
8159static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8160{
8161 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8162 return QgsHstoreUtils::build( map );
8163}
8164
8165static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8166{
8167 QVariantMap result;
8168 for ( int i = 0; i + 1 < values.length(); i += 2 )
8169 {
8170 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
8171 }
8172 return result;
8173}
8174
8175static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8176{
8177 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8178 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8179 QVariantMap resultMap;
8180
8181 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8182 {
8183 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
8184 }
8185
8186 return resultMap;
8187}
8188
8189static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8190{
8191 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
8192}
8193
8194static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8195{
8196 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
8197}
8198
8199static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8200{
8201 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8202 map.remove( values.at( 1 ).toString() );
8203 return map;
8204}
8205
8206static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8207{
8208 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8209 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
8210 return map;
8211}
8212
8213static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8214{
8215 QVariantMap result;
8216 for ( const QVariant &cur : values )
8217 {
8218 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
8219 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
8220 result.insert( it.key(), it.value() );
8221 }
8222 return result;
8223}
8224
8225static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8226{
8227 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
8228}
8229
8230static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8231{
8232 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
8233}
8234
8235static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8236{
8237 const QString envVarName = values.at( 0 ).toString();
8238 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
8239 return QVariant();
8240
8241 return QProcessEnvironment::systemEnvironment().value( envVarName );
8242}
8243
8244static QVariant fcnBaseFileName( 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( "base_file_name"_L1 ) );
8250 return QVariant();
8251 }
8252 return QFileInfo( file ).completeBaseName();
8253}
8254
8255static QVariant fcnFileSuffix( 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_suffix"_L1 ) );
8261 return QVariant();
8262 }
8263 return QFileInfo( file ).completeSuffix();
8264}
8265
8266static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8267{
8268 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8269 if ( parent->hasEvalError() )
8270 {
8271 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8272 return QVariant();
8273 }
8274 return QFileInfo::exists( file );
8275}
8276
8277static QVariant fcnFileName( 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( "file_name"_L1 ) );
8283 return QVariant();
8284 }
8285 return QFileInfo( file ).fileName();
8286}
8287
8288static QVariant fcnPathIsFile( 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( "is_file"_L1 ) );
8294 return QVariant();
8295 }
8296 return QFileInfo( file ).isFile();
8297}
8298
8299static QVariant fcnPathIsDir( 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( "is_directory"_L1 ) );
8305 return QVariant();
8306 }
8307 return QFileInfo( file ).isDir();
8308}
8309
8310static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8311{
8312 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8313 if ( parent->hasEvalError() )
8314 {
8315 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8316 return QVariant();
8317 }
8318 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8319}
8320
8321static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8322{
8323 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8324 if ( parent->hasEvalError() )
8325 {
8326 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8327 return QVariant();
8328 }
8329 return QFileInfo( file ).size();
8330}
8331
8332static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8333{
8334 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8335}
8336
8337static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8338{
8339 QVariant hash;
8340 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8341 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8342
8343 if ( method == "md4"_L1 )
8344 {
8345 hash = fcnHash( str, QCryptographicHash::Md4 );
8346 }
8347 else if ( method == "md5"_L1 )
8348 {
8349 hash = fcnHash( str, QCryptographicHash::Md5 );
8350 }
8351 else if ( method == "sha1"_L1 )
8352 {
8353 hash = fcnHash( str, QCryptographicHash::Sha1 );
8354 }
8355 else if ( method == "sha224"_L1 )
8356 {
8357 hash = fcnHash( str, QCryptographicHash::Sha224 );
8358 }
8359 else if ( method == "sha256"_L1 )
8360 {
8361 hash = fcnHash( str, QCryptographicHash::Sha256 );
8362 }
8363 else if ( method == "sha384"_L1 )
8364 {
8365 hash = fcnHash( str, QCryptographicHash::Sha384 );
8366 }
8367 else if ( method == "sha512"_L1 )
8368 {
8369 hash = fcnHash( str, QCryptographicHash::Sha512 );
8370 }
8371 else if ( method == "sha3_224"_L1 )
8372 {
8373 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8374 }
8375 else if ( method == "sha3_256"_L1 )
8376 {
8377 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8378 }
8379 else if ( method == "sha3_384"_L1 )
8380 {
8381 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8382 }
8383 else if ( method == "sha3_512"_L1 )
8384 {
8385 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8386 }
8387 else if ( method == "keccak_224"_L1 )
8388 {
8389 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8390 }
8391 else if ( method == "keccak_256"_L1 )
8392 {
8393 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8394 }
8395 else if ( method == "keccak_384"_L1 )
8396 {
8397 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8398 }
8399 else if ( method == "keccak_512"_L1 )
8400 {
8401 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8402 }
8403 else
8404 {
8405 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8406 }
8407 return hash;
8408}
8409
8410static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8411{
8412 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8413}
8414
8415static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8416{
8417 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8418}
8419
8420static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8421{
8422 const QByteArray input = values.at( 0 ).toByteArray();
8423 return QVariant( QString( input.toBase64() ) );
8424}
8425
8426static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8427{
8428 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8429 QUrlQuery query;
8430 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8431 {
8432 query.addQueryItem( it.key(), it.value().toString() );
8433 }
8434 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8436
8437static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8438{
8439 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8440 const QByteArray base64 = value.toLocal8Bit();
8441 const QByteArray decoded = QByteArray::fromBase64( base64 );
8442 return QVariant( decoded );
8443}
8444
8446typedef std::function<bool( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend )> RelationFunction;
8447
8448static QVariant executeGeomOverlay(
8449 const QVariantList &values,
8450 const QgsExpressionContext *context,
8451 QgsExpression *parent,
8452 const RelationFunction &relationFunction,
8453 bool invert = false,
8454 double bboxGrow = 0,
8455 bool isNearestFunc = false,
8456 bool isIntersectsFunc = false
8457)
8458{
8459 if ( !context )
8460 {
8461 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8462 return QVariant();
8463 }
8464
8465 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8466 // TODO this function is NOT thread safe
8468 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8470
8471 QgsFeatureRequest request;
8472 request.setTimeout( 10000 );
8473 request.setRequestMayBeNested( true );
8474 request.setFeedback( context->feedback() );
8475
8476 // First parameter is the overlay layer
8477 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8479
8480 const bool layerCanBeCached = node->isStatic( parent, context );
8481 QVariant targetLayerValue = node->eval( parent, context );
8483
8484 // Second parameter is the expression to evaluate (or null for testonly)
8485 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8487 QString subExpString = node->dump();
8488
8489 bool testOnly = ( subExpString == "NULL" );
8490 // TODO this function is NOT thread safe
8492 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8494 if ( !targetLayer ) // No layer, no joy
8495 {
8496 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8497 return QVariant();
8498 }
8499
8500 // Third parameter is the filtering expression
8501 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8503 QString filterString = node->dump();
8504 if ( filterString != "NULL" )
8505 {
8506 request.setFilterExpression( filterString ); //filter cached features
8507 }
8508
8509 // Fourth parameter is the limit
8510 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8512 QVariant limitValue = node->eval( parent, context );
8514 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8515
8516 double max_distance = 0;
8517 bool cacheEnabled = false;
8518
8519 double minOverlap { -1 };
8520 double minInscribedCircleRadius { -1 };
8521 bool returnDetails = false; //#spellok
8522 bool sortByMeasure = false;
8523 bool sortAscending = false;
8524 bool requireMeasures = false;
8525 bool overlapOrRadiusFilter = false;
8526
8528
8529 if ( isNearestFunc ) //maxdistance param handling
8530 {
8531 // Fifth parameter (for nearest only) is the max distance
8532 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8534 QVariant distanceValue = node->eval( parent, context );
8536 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8537
8538 // Sixth (for nearest only) parameter is the cache toggle
8539 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
8541 QVariant cacheValue = node->eval( parent, context );
8543 cacheEnabled = cacheValue.toBool();
8544 }
8545 else
8546 {
8547 // Fifth parameter is the cache toggle
8548 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8550 QVariant cacheValue = node->eval( parent, context );
8552 cacheEnabled = cacheValue.toBool();
8553
8554 // Sixth parameter is the min overlap (area or length)
8555 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8557 const QVariant minOverlapValue = node->eval( parent, context );
8559 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8560
8561 // Seventh parameter is the min inscribed circle radius
8562 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8564 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8566 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8567
8568 // Eighth parameter is the return_details
8569 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8570 // Return measures is only effective when an expression is set
8571 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8572
8573 // Ninth parameter is the sort_by_intersection_size flag
8574 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8575 // Sort by measures is only effective when an expression is set
8576 const QString sorting { node->eval( parent, context ).toString().toLower() };
8577 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8578 sortAscending = sorting.startsWith( "asc" );
8579 requireMeasures = sortByMeasure || returnDetails; //#spellok
8580 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8581
8582 // Tenth parameter is the geometry backend
8583 node = QgsExpressionUtils::getNode( values.at( 9 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8585 const QString backendStr = node->eval( parent, context ).toString().toUpper();
8587
8588 bool ok;
8589 backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
8590 if ( !ok )
8591 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
8592 }
8593
8594 FEAT_FROM_CONTEXT( context, feat )
8595 const QgsGeometry geometry = feat.geometry();
8596
8597 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8598 {
8599 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8600 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8601 }
8602
8603 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8604
8605 QgsRectangle intDomain = geometry.boundingBox();
8606 if ( bboxGrow != 0 )
8607 {
8608 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8609 }
8610
8611 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8612
8613 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8614 // Otherwise, it can be toggled by the user
8615 QgsSpatialIndex spatialIndex;
8616 QgsVectorLayer *cachedTarget;
8617 QList<QgsFeature> features;
8618 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8619 {
8620 // If the cache (local spatial index) is enabled, we materialize the whole
8621 // layer, then do the request on that layer instead.
8622 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8623 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8624
8625 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8626 {
8627 cachedTarget = targetLayer->materialize( request );
8628 if ( layerCanBeCached )
8629 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8630 }
8631 else
8632 {
8633 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8634 }
8635
8636 if ( !context->hasCachedValue( cacheIndex ) )
8637 {
8638 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8639 if ( layerCanBeCached )
8640 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8641 }
8642 else
8643 {
8644 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8645 }
8646
8647 QList<QgsFeatureId> fidsList;
8648 if ( isNearestFunc )
8649 {
8650 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8651 }
8652 else
8653 {
8654 fidsList = spatialIndex.intersects( intDomain );
8655 }
8656
8657 QListIterator<QgsFeatureId> i( fidsList );
8658 while ( i.hasNext() )
8659 {
8660 QgsFeatureId fId2 = i.next();
8661 if ( sameLayers && feat.id() == fId2 )
8662 continue;
8663 features.append( cachedTarget->getFeature( fId2 ) );
8664 }
8665 }
8666 else
8667 {
8668 // If the cache (local spatial index) is not enabled, we directly
8669 // get the features from the target layer
8670 request.setFilterRect( intDomain );
8671 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8672 QgsFeature feat2;
8673 while ( fit.nextFeature( feat2 ) )
8674 {
8675 if ( sameLayers && feat.id() == feat2.id() )
8676 continue;
8677 features.append( feat2 );
8678 }
8679 }
8680
8681 QgsExpression subExpression;
8682 QgsExpressionContext subContext;
8683 if ( !testOnly )
8684 {
8685 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8686 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8687
8688 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8689 {
8690 subExpression = QgsExpression( subExpString );
8692 subExpression.prepare( &subContext );
8693 }
8694 else
8695 {
8696 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8697 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8698 }
8699 }
8700
8701 // //////////////////////////////////////////////////////////////////
8702 // Helper functions for geometry tests
8703
8704 // Test function for linestring geometries, returns TRUE if test passes
8705 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double &overlapValue ) -> bool {
8706 bool testResult { false };
8707 // For return measures:
8708 QVector<double> overlapValues;
8709 const QgsGeometry merged { intersection.mergeLines() };
8710 for ( auto it = merged.const_parts_begin(); !testResult && it != merged.const_parts_end(); ++it )
8711 {
8713 // Check min overlap for intersection (if set)
8714 if ( minOverlap != -1 || requireMeasures )
8715 {
8716 overlapValue = geom->length();
8717 overlapValues.append( overlapValue );
8718 if ( minOverlap != -1 )
8719 {
8720 if ( overlapValue >= minOverlap )
8721 {
8722 testResult = true;
8723 }
8724 else
8725 {
8726 continue;
8727 }
8728 }
8729 }
8730 }
8731
8732 if ( !overlapValues.isEmpty() )
8733 {
8734 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8735 }
8736
8737 return testResult;
8738 };
8739
8740 // Test function for polygon geometries, returns TRUE if test passes
8741 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double &radiusValue, double &overlapValue ) -> bool {
8742 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8743 bool testResult { false };
8744 // For return measures:
8745 QVector<double> overlapValues;
8746 QVector<double> radiusValues;
8747 for ( auto it = intersection.const_parts_begin(); ( !testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8748 {
8750 // Check min overlap for intersection (if set)
8751 if ( minOverlap != -1 || requireMeasures )
8752 {
8753 overlapValue = geom->area();
8754 overlapValues.append( geom->area() );
8755 if ( minOverlap != -1 )
8756 {
8757 if ( overlapValue >= minOverlap )
8758 {
8759 testResult = true;
8760 }
8761 else
8762 {
8763 continue;
8764 }
8765 }
8766 }
8767
8768 // Check min inscribed circle radius for intersection (if set)
8769 if ( minInscribedCircleRadius != -1 || requireMeasures )
8770 {
8771 const QgsRectangle bbox = geom->boundingBox();
8772 const double width = bbox.width();
8773 const double height = bbox.height();
8774 const double size = width > height ? width : height;
8775 const double tolerance = size / 100.0;
8776 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8777 testResult = radiusValue >= minInscribedCircleRadius;
8778 radiusValues.append( radiusValues );
8779 }
8780 } // end for parts
8781
8782 // Get the max values
8783 if ( !radiusValues.isEmpty() )
8784 {
8785 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8786 }
8787
8788 if ( !overlapValues.isEmpty() )
8789 {
8790 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8791 }
8792
8793 return testResult;
8794 };
8795
8796
8797 bool found = false;
8798 int foundCount = 0;
8799 QVariantList results;
8800
8801 QListIterator<QgsFeature> i( features );
8802 try
8803 {
8804 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8805 {
8806 QgsFeature feat2 = i.next();
8807
8808
8809 if ( relationFunction( geometry, feat2.geometry(), values, backend ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8810 {
8811 double overlapValue = -1;
8812 double radiusValue = -1;
8813
8814 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8815 {
8816 QgsGeometry intersection { geometry.intersection( feat2.geometry(), QgsGeometryParameters() ) };
8817
8818 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8819 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8820 {
8821 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8822 intersection = QgsGeometry();
8823 QgsMultiPolygonXY poly;
8824 QgsMultiPolylineXY line;
8825 QgsMultiPointXY point;
8826 for ( const auto &geom : std::as_const( geometries ) )
8827 {
8828 switch ( geom.type() )
8829 {
8831 {
8832 poly.append( geom.asPolygon() );
8833 break;
8834 }
8836 {
8837 line.append( geom.asPolyline() );
8838 break;
8839 }
8841 {
8842 point.append( geom.asPoint() );
8843 break;
8844 }
8847 {
8848 break;
8849 }
8850 }
8851 }
8852
8853 switch ( geometry.type() )
8854 {
8856 {
8857 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8858 break;
8859 }
8861 {
8862 intersection = QgsGeometry::fromMultiPolylineXY( line );
8863 break;
8864 }
8866 {
8867 intersection = QgsGeometry::fromMultiPointXY( point );
8868 break;
8869 }
8872 {
8873 break;
8874 }
8875 }
8876 }
8877
8878 // Depending on the intersection geometry type and on the geometry type of
8879 // the tested geometry we can run different tests and collect different measures
8880 // that can be used for sorting (if required).
8881 switch ( intersection.type() )
8882 {
8884 {
8885 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8886 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8887
8888 if ( !testResult && overlapOrRadiusFilter )
8889 {
8890 continue;
8891 }
8892
8893 break;
8894 }
8895
8897 {
8898 // If the intersection is a linestring and a minimum circle is required
8899 // we can discard this result immediately.
8900 if ( minInscribedCircleRadius != -1 )
8901 {
8902 continue;
8903 }
8904
8905 // Otherwise a test for the overlap value is performed.
8906 const bool testResult { testLinestring( intersection, overlapValue ) };
8907
8908 if ( !testResult && overlapOrRadiusFilter )
8909 {
8910 continue;
8911 }
8912
8913 break;
8914 }
8915
8917 {
8918 // If the intersection is a point and a minimum circle is required
8919 // we can discard this result immediately.
8920 if ( minInscribedCircleRadius != -1 )
8921 {
8922 continue;
8923 }
8924
8925 bool testResult { false };
8926 if ( minOverlap != -1 || requireMeasures )
8927 {
8928 // Initially set this to 0 because it's a point intersection...
8929 overlapValue = 0;
8930 // ... but if the target geometry is not a point and the source
8931 // geometry is a point, we must record the length or the area
8932 // of the intersected geometry and use that as a measure for
8933 // sorting or reporting.
8934 if ( geometry.type() == Qgis::GeometryType::Point )
8935 {
8936 switch ( feat2.geometry().type() )
8937 {
8941 {
8942 break;
8943 }
8945 {
8946 testResult = testLinestring( feat2.geometry(), overlapValue );
8947 break;
8948 }
8950 {
8951 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8952 break;
8953 }
8954 }
8955 }
8956
8957 if ( !testResult && overlapOrRadiusFilter )
8958 {
8959 continue;
8960 }
8961 }
8962 break;
8963 }
8964
8967 {
8968 continue;
8969 }
8970 }
8971 }
8972
8973 found = true;
8974 foundCount++;
8975
8976 // We just want a single boolean result if there is any intersect: finish and return true
8977 if ( testOnly )
8978 break;
8979
8980 if ( !invert )
8981 {
8982 // We want a list of attributes / geometries / other expression values, evaluate now
8983 subContext.setFeature( feat2 );
8984 const QVariant expResult = subExpression.evaluate( &subContext );
8985
8986 if ( requireMeasures )
8987 {
8988 QVariantMap resultRecord;
8989 resultRecord.insert( u"id"_s, feat2.id() );
8990 resultRecord.insert( u"result"_s, expResult );
8991 // Overlap is always added because return measures was set
8992 resultRecord.insert( u"overlap"_s, overlapValue );
8993 // Radius is only added when is different than -1 (because for linestrings is not set)
8994 if ( radiusValue != -1 )
8995 {
8996 resultRecord.insert( u"radius"_s, radiusValue );
8997 }
8998 results.append( resultRecord );
8999 }
9000 else
9001 {
9002 results.append( expResult );
9003 }
9004 }
9005 else
9006 {
9007 // If not, results is a list of found ids, which we'll inverse and evaluate below
9008 results.append( feat2.id() );
9009 }
9010 }
9011 }
9012 }
9013 catch ( QgsException &e )
9014 {
9015 parent->setEvalErrorString( e.what() );
9016 return false;
9017 }
9018
9019 if ( testOnly )
9020 {
9021 if ( invert )
9022 found = !found; //for disjoint condition
9023 return found;
9024 }
9025
9026 if ( !invert )
9027 {
9028 if ( requireMeasures )
9029 {
9030 if ( sortByMeasure )
9031 {
9032 std::sort( results.begin(), results.end(), [sortAscending]( const QVariant &recordA, const QVariant &recordB ) -> bool {
9033 return sortAscending ? recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
9034 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
9035 } );
9036 }
9037 // Resize
9038 if ( limit > 0 && results.size() > limit )
9039 {
9040 results.erase( results.begin() + limit );
9041 }
9042
9043 if ( !returnDetails ) //#spellok
9044 {
9045 QVariantList expResults;
9046 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
9047 {
9048 expResults.append( it->toMap().value( u"result"_s ) );
9049 }
9050 return expResults;
9051 }
9052 }
9053
9054 return results;
9055 }
9056
9057 // for disjoint condition returns the results for cached layers not intersected feats
9058 QVariantList disjoint_results;
9059 QgsFeature feat2;
9060 QgsFeatureRequest request2;
9061 request2.setLimit( limit );
9062 if ( context )
9063 request2.setFeedback( context->feedback() );
9064 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
9065 while ( fi.nextFeature( feat2 ) )
9066 {
9067 if ( !results.contains( feat2.id() ) )
9068 {
9069 subContext.setFeature( feat2 );
9070 disjoint_results.append( subExpression.evaluate( &subContext ) );
9071 }
9072 }
9073 return disjoint_results;
9074}
9075
9076// Intersect functions:
9077
9078static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9079{
9080 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9081 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, false, true );
9082}
9083
9084static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9085{
9086 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.contains( other ); };
9087 return executeGeomOverlay( values, context, parent, geomFunction );
9088}
9089
9090static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9091{
9092 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.crosses( other ); };
9093 return executeGeomOverlay( values, context, parent, geomFunction );
9094}
9095
9096static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9097{
9098 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9099 return geometry.isExactlyEqual( other, Qgis::GeometryBackend::QGIS );
9100 };
9101 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9102}
9103
9104static QVariant fcnGeomOverlayEqualsExact( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9105{
9106 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9107 return geometry.isExactlyEqual( other, backend );
9108 };
9109 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9110}
9111
9112static QVariant fcnGeomOverlayEqualsTopological( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9113{
9114 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9115 return geometry.isTopologicallyEqual( other, backend );
9116 };
9117 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9118}
9119
9120static QVariant fcnGeomOverlayEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9121{
9122 // This parameter is the epsilon tolerance
9123 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 10 ), parent );
9125 QVariant epsilonValue = node->eval( parent, context );
9127 double epsilon = QgsExpressionUtils::getDoubleValue( epsilonValue, parent );
9128
9129 RelationFunction geomFunction = [epsilon]( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9130 return geometry.isFuzzyEqual( other, epsilon, backend );
9131 };
9132 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9133}
9134
9135static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9136{
9137 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.touches( other ); };
9138 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9139}
9140
9141static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9142{
9143 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.within( other ); };
9144 return executeGeomOverlay( values, context, parent, geomFunction );
9145}
9146
9147static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9148{
9149 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9150 return executeGeomOverlay( values, context, parent, geomFunction, true, 0, false, true );
9151}
9152
9153static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9154{
9155 RelationFunction geomFunction = []( const QgsGeometry &, const QgsGeometry &, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9156 return true; // does nothing on purpose
9157 };
9158 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, true );
9159}
9160
9161const QList<QgsExpressionFunction *> &QgsExpression::Functions()
9162{
9163 // The construction of the list isn't thread-safe, and without the mutex,
9164 // crashes in the WFS provider may occur, since it can parse expressions
9165 // in parallel.
9166 // The mutex needs to be recursive.
9167 QMutexLocker locker( &sFunctionsMutex );
9168
9169 QList<QgsExpressionFunction *> &functions = *sFunctions();
9170
9171 if ( functions.isEmpty() )
9172 {
9174 << QgsExpressionFunction::Parameter( u"expression"_s )
9175 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
9176 << QgsExpressionFunction::Parameter( u"filter"_s, true );
9177
9178 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
9179 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true ) << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9180
9181 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
9182 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9183
9184 functions
9185 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
9186 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
9187 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
9188 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnAzimuth, u"GeometryGroup"_s )
9189 << new QgsStaticExpressionFunction(
9190 u"bearing"_s,
9192 << QgsExpressionFunction::Parameter( u"point1"_s )
9193 << QgsExpressionFunction::Parameter( u"point2"_s )
9194 << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() )
9195 << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ),
9196 fcnBearing,
9197 u"GeometryGroup"_s
9198 )
9199 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnInclination, u"GeometryGroup"_s )
9200 << new QgsStaticExpressionFunction(
9201 u"project"_s,
9203 << QgsExpressionFunction::Parameter( u"point"_s )
9204 << QgsExpressionFunction::Parameter( u"distance"_s )
9205 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9206 << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ),
9207 fcnProject,
9208 u"GeometryGroup"_s
9209 )
9210 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
9211 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
9212 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
9213 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
9214 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
9215 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
9216 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
9217 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
9218 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
9219 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
9220 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
9221 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
9222 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
9223
9224 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction(
9225 u"rand"_s,
9226 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9227 fcnRnd,
9228 u"Math"_s
9229 );
9230 randFunc->setIsStatic( false );
9231 functions << randFunc;
9232
9233 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction(
9234 u"randf"_s,
9236 << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 )
9237 << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 )
9238 << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9239 fcnRndF,
9240 u"Math"_s
9241 );
9242 randfFunc->setIsStatic( false );
9243 functions << randfFunc;
9244
9245 functions
9246 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9247 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9248 << 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 )
9249 << new QgsStaticExpressionFunction(
9250 u"scale_linear"_s,
9252 << QgsExpressionFunction::Parameter( u"value"_s )
9253 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9254 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9255 << QgsExpressionFunction::Parameter( u"range_min"_s )
9256 << QgsExpressionFunction::Parameter( u"range_max"_s ),
9257 fcnLinearScale,
9258 u"Math"_s
9259 )
9260 << new QgsStaticExpressionFunction(
9261 u"scale_polynomial"_s,
9263 << QgsExpressionFunction::Parameter( u"value"_s )
9264 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9265 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9266 << QgsExpressionFunction::Parameter( u"range_min"_s )
9267 << QgsExpressionFunction::Parameter( u"range_max"_s )
9268 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9269 fcnPolynomialScale,
9270 u"Math"_s,
9271 QString(),
9272 false,
9273 QSet<QString>(),
9274 false,
9275 QStringList() << u"scale_exp"_s
9276 )
9277 << new QgsStaticExpressionFunction(
9278 u"scale_exponential"_s,
9280 << QgsExpressionFunction::Parameter( u"value"_s )
9281 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9282 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9283 << QgsExpressionFunction::Parameter( u"range_min"_s )
9284 << QgsExpressionFunction::Parameter( u"range_max"_s )
9285 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9286 fcnExponentialScale,
9287 u"Math"_s
9288 )
9289 << new QgsStaticExpressionFunction(
9290 u"scale_cubic_bezier"_s,
9292 << QgsExpressionFunction::Parameter( u"value"_s )
9293 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9294 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9295 << QgsExpressionFunction::Parameter( u"range_min"_s )
9296 << QgsExpressionFunction::Parameter( u"range_max"_s )
9297 << QgsExpressionFunction::Parameter( u"x1"_s )
9298 << QgsExpressionFunction::Parameter( u"y1"_s )
9299 << QgsExpressionFunction::Parameter( u"x2"_s )
9300 << QgsExpressionFunction::Parameter( u"y2"_s ),
9301 fcnCubicBezierScale,
9302 u"Math"_s
9303 )
9304 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
9305 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
9306 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
9307 << 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 )
9308 << 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 )
9309 << 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 )
9310 << new QgsStaticExpressionFunction(
9311 u"to_string"_s,
9312 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9313 fcnToString,
9314 QStringList() << u"Conversions"_s << u"String"_s,
9315 QString(),
9316 false,
9317 QSet<QString>(),
9318 false,
9319 QStringList() << u"tostring"_s
9320 )
9321 << new QgsStaticExpressionFunction(
9322 u"to_datetime"_s,
9324 << QgsExpressionFunction::Parameter( u"value"_s )
9325 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9326 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9327 fcnToDateTime,
9328 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9329 QString(),
9330 false,
9331 QSet<QString>(),
9332 false,
9333 QStringList() << u"todatetime"_s
9334 )
9335 << new QgsStaticExpressionFunction(
9336 u"to_date"_s,
9338 << QgsExpressionFunction::Parameter( u"value"_s )
9339 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9340 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9341 fcnToDate,
9342 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9343 QString(),
9344 false,
9345 QSet<QString>(),
9346 false,
9347 QStringList() << u"todate"_s
9348 )
9349 << new QgsStaticExpressionFunction(
9350 u"to_time"_s,
9352 << QgsExpressionFunction::Parameter( u"value"_s )
9353 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9354 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9355 fcnToTime,
9356 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9357 QString(),
9358 false,
9359 QSet<QString>(),
9360 false,
9361 QStringList() << u"totime"_s
9362 )
9363 << new QgsStaticExpressionFunction(
9364 u"to_interval"_s,
9365 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9366 fcnToInterval,
9367 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9368 QString(),
9369 false,
9370 QSet<QString>(),
9371 false,
9372 QStringList() << u"tointerval"_s
9373 )
9374 << new QgsStaticExpressionFunction(
9375 u"to_dm"_s,
9377 << QgsExpressionFunction::Parameter( u"value"_s )
9378 << QgsExpressionFunction::Parameter( u"axis"_s )
9379 << QgsExpressionFunction::Parameter( u"precision"_s )
9380 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9381 fcnToDegreeMinute,
9382 u"Conversions"_s,
9383 QString(),
9384 false,
9385 QSet<QString>(),
9386 false,
9387 QStringList() << u"todm"_s
9388 )
9389 << new QgsStaticExpressionFunction(
9390 u"to_dms"_s,
9392 << QgsExpressionFunction::Parameter( u"value"_s )
9393 << QgsExpressionFunction::Parameter( u"axis"_s )
9394 << QgsExpressionFunction::Parameter( u"precision"_s )
9395 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9396 fcnToDegreeMinuteSecond,
9397 u"Conversions"_s,
9398 QString(),
9399 false,
9400 QSet<QString>(),
9401 false,
9402 QStringList() << u"todms"_s
9403 )
9404 << 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 )
9405 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
9406 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
9407 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
9408 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9409 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
9410 << new QgsStaticExpressionFunction(
9411 u"if"_s,
9413 << QgsExpressionFunction::Parameter( u"condition"_s )
9414 << QgsExpressionFunction::Parameter( u"result_when_true"_s )
9415 << QgsExpressionFunction::Parameter( u"result_when_false"_s ),
9416 fcnIf,
9417 u"Conditionals"_s,
9418 QString(),
9419 false,
9420 QSet<QString>(),
9421 true
9422 )
9423 << new QgsStaticExpressionFunction(
9424 u"try"_s,
9425 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ),
9426 fcnTry,
9427 u"Conditionals"_s,
9428 QString(),
9429 false,
9430 QSet<QString>(),
9431 true
9432 )
9433
9434 << new QgsStaticExpressionFunction(
9435 u"aggregate"_s,
9437 << QgsExpressionFunction::Parameter( u"layer"_s )
9438 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9439 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9440 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9441 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9442 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9443 fcnAggregate,
9444 u"Aggregates"_s,
9445 QString(),
9446 []( const QgsExpressionNodeFunction *node ) {
9447 // usesGeometry callback: return true if @parent variable is referenced
9448
9449 if ( !node )
9450 return true;
9451
9452 if ( !node->args() )
9453 return false;
9454
9455 QSet<QString> referencedVars;
9456 if ( node->args()->count() > 2 )
9457 {
9458 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9459 referencedVars = subExpressionNode->referencedVariables();
9460 }
9461
9462 if ( node->args()->count() > 3 )
9463 {
9464 QgsExpressionNode *filterNode = node->args()->at( 3 );
9465 referencedVars.unite( filterNode->referencedVariables() );
9466 }
9467 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
9468 },
9469 []( const QgsExpressionNodeFunction *node ) {
9470 // referencedColumns callback: return AllAttributes if @parent variable is referenced
9471
9472 if ( !node )
9473 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9474
9475 if ( !node->args() )
9476 return QSet<QString>();
9477
9478 QSet<QString> referencedCols;
9479 QSet<QString> referencedVars;
9480
9481 if ( node->args()->count() > 2 )
9482 {
9483 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9484 referencedVars = subExpressionNode->referencedVariables();
9485 referencedCols = subExpressionNode->referencedColumns();
9486 }
9487 if ( node->args()->count() > 3 )
9488 {
9489 QgsExpressionNode *filterNode = node->args()->at( 3 );
9490 referencedVars = filterNode->referencedVariables();
9491 referencedCols.unite( filterNode->referencedColumns() );
9492 }
9493
9494 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
9495 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9496 else
9497 return referencedCols;
9498 },
9499 true
9500 )
9501
9502 << new QgsStaticExpressionFunction(
9503 u"relation_aggregate"_s,
9505 << QgsExpressionFunction::Parameter( u"relation"_s )
9506 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9507 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9508 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9509 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9510 fcnAggregateRelation,
9511 u"Aggregates"_s,
9512 QString(),
9513 false,
9514 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9515 true
9516 )
9517
9518 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9519 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9520 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9521 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9522 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9523 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9524 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9525 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9526 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9527 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9528 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9529 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9530 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9531 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9532 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9533 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9534 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9535 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9536 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9537 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9538 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9539
9540 << 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 )
9541 << new QgsStaticExpressionFunction(
9542 u"regexp_matches"_s,
9544 << QgsExpressionFunction::Parameter( u"string"_s )
9545 << QgsExpressionFunction::Parameter( u"regex"_s )
9546 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
9547 fcnRegexpMatches,
9548 u"Arrays"_s
9549 )
9550
9551 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9552 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s ) << QgsExpressionFunction::Parameter( u"datetime2"_s ), fcnAge, u"Date and Time"_s )
9553 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9554 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9555 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9556 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9557 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9558 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9559 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9560 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9561 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9562 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9563 << 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 )
9564 << new QgsStaticExpressionFunction(
9565 u"make_time"_s,
9566 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s ) << QgsExpressionFunction::Parameter( u"minute"_s ) << QgsExpressionFunction::Parameter( u"second"_s ),
9567 fcnMakeTime,
9568 u"Date and Time"_s
9569 )
9570 << new QgsStaticExpressionFunction(
9571 u"make_datetime"_s,
9573 << QgsExpressionFunction::Parameter( u"year"_s )
9574 << QgsExpressionFunction::Parameter( u"month"_s )
9575 << QgsExpressionFunction::Parameter( u"day"_s )
9576 << QgsExpressionFunction::Parameter( u"hour"_s )
9577 << QgsExpressionFunction::Parameter( u"minute"_s )
9578 << QgsExpressionFunction::Parameter( u"second"_s ),
9579 fcnMakeDateTime,
9580 u"Date and Time"_s
9581 )
9582 << new QgsStaticExpressionFunction(
9583 u"make_interval"_s,
9585 << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9586 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9587 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9588 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9589 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9590 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9591 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9592 fcnMakeInterval,
9593 u"Date and Time"_s
9594 )
9595 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9596 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9597 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9598 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9599 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9600 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9601 << new QgsStaticExpressionFunction(
9602 u"substr_count"_s,
9604 << QgsExpressionFunction::Parameter( u"string"_s )
9605 << QgsExpressionFunction::Parameter( u"substring"_s )
9606 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9607 fcnSubstrCount,
9608 u"String"_s
9609 )
9610 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9611 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9612 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9613 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9614 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9615 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9616 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9617 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9618 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9619 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9620 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9621 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9622 << new QgsStaticExpressionFunction(
9623 u"wordwrap"_s,
9625 << QgsExpressionFunction::Parameter( u"text"_s )
9626 << QgsExpressionFunction::Parameter( u"length"_s )
9627 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ),
9628 fcnWordwrap,
9629 u"String"_s
9630 )
9631 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9632 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9633 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s ) }, fcnRepeat, u"String"_s )
9634 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9635 << new QgsStaticExpressionFunction(
9636 u"regexp_replace"_s,
9637 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"replacement"_s ),
9638 fcnRegexpReplace,
9639 u"String"_s
9640 )
9641 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9642 << new QgsStaticExpressionFunction(
9643 u"substr"_s,
9644 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ),
9645 fcnSubstr,
9646 u"String"_s,
9647 QString(),
9648 false,
9649 QSet< QString >(),
9650 false,
9651 QStringList(),
9652 true
9653 )
9654 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9655 << new QgsStaticExpressionFunction( u"concat_ws"_s, -1, fcnConcatWs, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9656 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9657 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9658 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9659 << new QgsStaticExpressionFunction(
9660 u"rpad"_s,
9661 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s, true, u" "_s ),
9662 fcnRPad,
9663 u"String"_s
9664 )
9665 << new QgsStaticExpressionFunction(
9666 u"lpad"_s,
9667 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s, true, u" "_s ),
9668 fcnLPad,
9669 u"String"_s
9670 )
9671 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9672 << new QgsStaticExpressionFunction(
9673 u"format_number"_s,
9675 << QgsExpressionFunction::Parameter( u"number"_s )
9676 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9677 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9678 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9679 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ),
9680 fcnFormatNumber,
9681 u"String"_s
9682 )
9683 << new QgsStaticExpressionFunction(
9684 u"format_date"_s,
9686 << QgsExpressionFunction::Parameter( u"datetime"_s )
9687 << QgsExpressionFunction::Parameter( u"format"_s )
9688 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9689 fcnFormatDate,
9690 QStringList() << u"String"_s << u"Date and Time"_s
9691 )
9692 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9693 << new QgsStaticExpressionFunction(
9694 u"color_mix_rgb"_s,
9695 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9696 fcnColorMixRgb,
9697 u"Color"_s
9698 )
9699 << new QgsStaticExpressionFunction(
9700 u"color_mix"_s,
9701 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9702 fcnColorMix,
9703 u"Color"_s
9704 )
9705 << 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 )
9706 << new QgsStaticExpressionFunction(
9707 u"color_rgbf"_s,
9709 << QgsExpressionFunction::Parameter( u"red"_s )
9710 << QgsExpressionFunction::Parameter( u"green"_s )
9711 << QgsExpressionFunction::Parameter( u"blue"_s )
9712 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9713 fcnColorRgbF,
9714 u"Color"_s
9715 )
9716 << new QgsStaticExpressionFunction(
9717 u"color_rgba"_s,
9719 << QgsExpressionFunction::Parameter( u"red"_s )
9720 << QgsExpressionFunction::Parameter( u"green"_s )
9721 << QgsExpressionFunction::Parameter( u"blue"_s )
9722 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9723 fncColorRgba,
9724 u"Color"_s
9725 )
9726 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColor, u"Color"_s )
9727 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColorObject, u"Color"_s )
9728 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ), fcnCreateRamp, u"Color"_s )
9729 << new QgsStaticExpressionFunction(
9730 u"color_hsl"_s,
9731 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"lightness"_s ),
9732 fcnColorHsl,
9733 u"Color"_s
9734 )
9735 << new QgsStaticExpressionFunction(
9736 u"color_hsla"_s,
9738 << QgsExpressionFunction::Parameter( u"hue"_s )
9739 << QgsExpressionFunction::Parameter( u"saturation"_s )
9740 << QgsExpressionFunction::Parameter( u"lightness"_s )
9741 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9742 fncColorHsla,
9743 u"Color"_s
9744 )
9745 << new QgsStaticExpressionFunction(
9746 u"color_hslf"_s,
9748 << QgsExpressionFunction::Parameter( u"hue"_s )
9749 << QgsExpressionFunction::Parameter( u"saturation"_s )
9750 << QgsExpressionFunction::Parameter( u"lightness"_s )
9751 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9752 fcnColorHslF,
9753 u"Color"_s
9754 )
9755 << new QgsStaticExpressionFunction(
9756 u"color_hsv"_s,
9757 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9758 fcnColorHsv,
9759 u"Color"_s
9760 )
9761 << new QgsStaticExpressionFunction(
9762 u"color_hsva"_s,
9764 << QgsExpressionFunction::Parameter( u"hue"_s )
9765 << QgsExpressionFunction::Parameter( u"saturation"_s )
9766 << QgsExpressionFunction::Parameter( u"value"_s )
9767 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9768 fncColorHsva,
9769 u"Color"_s
9770 )
9771 << new QgsStaticExpressionFunction(
9772 u"color_hsvf"_s,
9774 << QgsExpressionFunction::Parameter( u"hue"_s )
9775 << QgsExpressionFunction::Parameter( u"saturation"_s )
9776 << QgsExpressionFunction::Parameter( u"value"_s )
9777 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9778 fcnColorHsvF,
9779 u"Color"_s
9780 )
9781 << new QgsStaticExpressionFunction(
9782 u"color_cmyk"_s,
9784 << QgsExpressionFunction::Parameter( u"cyan"_s )
9785 << QgsExpressionFunction::Parameter( u"magenta"_s )
9786 << QgsExpressionFunction::Parameter( u"yellow"_s )
9787 << QgsExpressionFunction::Parameter( u"black"_s ),
9788 fcnColorCmyk,
9789 u"Color"_s
9790 )
9791 << new QgsStaticExpressionFunction(
9792 u"color_cmyka"_s,
9794 << QgsExpressionFunction::Parameter( u"cyan"_s )
9795 << QgsExpressionFunction::Parameter( u"magenta"_s )
9796 << QgsExpressionFunction::Parameter( u"yellow"_s )
9797 << QgsExpressionFunction::Parameter( u"black"_s )
9798 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9799 fncColorCmyka,
9800 u"Color"_s
9801 )
9802 << new QgsStaticExpressionFunction(
9803 u"color_cmykf"_s,
9805 << QgsExpressionFunction::Parameter( u"cyan"_s )
9806 << QgsExpressionFunction::Parameter( u"magenta"_s )
9807 << QgsExpressionFunction::Parameter( u"yellow"_s )
9808 << QgsExpressionFunction::Parameter( u"black"_s )
9809 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9810 fcnColorCmykF,
9811 u"Color"_s
9812 )
9813 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ), fncColorPart, u"Color"_s )
9814 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncDarker, u"Color"_s )
9815 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncLighter, u"Color"_s )
9816 << new QgsStaticExpressionFunction(
9817 u"set_color_part"_s,
9818 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9819 fncSetColorPart,
9820 u"Color"_s
9821 )
9822
9823 // file info
9824 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnBaseFileName, u"Files and Paths"_s )
9825 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSuffix, u"Files and Paths"_s )
9826 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileExists, u"Files and Paths"_s )
9827 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileName, u"Files and Paths"_s )
9828 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsFile, u"Files and Paths"_s )
9829 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsDir, u"Files and Paths"_s )
9830 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFilePath, u"Files and Paths"_s )
9831 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSize, u"Files and Paths"_s )
9832
9833 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ), fcnExif, u"Files and Paths"_s )
9834 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnExifGeoTag, u"GeometryGroup"_s )
9835
9836 // hash
9837 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ), fcnGenericHash, u"Conversions"_s )
9838 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashMd5, u"Conversions"_s )
9839 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashSha256, u"Conversions"_s )
9840
9841 //base64
9842 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBase64, u"Conversions"_s )
9843 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnFromBase64, u"Conversions"_s )
9844
9845 // magnetic models
9846 << new QgsStaticExpressionFunction(
9847 u"magnetic_declination"_s,
9849 << QgsExpressionFunction::Parameter( u"model_name"_s )
9850 << QgsExpressionFunction::Parameter( u"date"_s )
9851 << QgsExpressionFunction::Parameter( u"latitude"_s )
9852 << QgsExpressionFunction::Parameter( u"longitude"_s )
9853 << QgsExpressionFunction::Parameter( u"height"_s )
9854 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9855 fcnMagneticDeclination,
9856 u"MagneticModels"_s
9857 )
9858 << new QgsStaticExpressionFunction(
9859 u"magnetic_inclination"_s,
9861 << QgsExpressionFunction::Parameter( u"model_name"_s )
9862 << QgsExpressionFunction::Parameter( u"date"_s )
9863 << QgsExpressionFunction::Parameter( u"latitude"_s )
9864 << QgsExpressionFunction::Parameter( u"longitude"_s )
9865 << QgsExpressionFunction::Parameter( u"height"_s )
9866 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9867 fcnMagneticInclination,
9868 u"MagneticModels"_s
9869 )
9870 << new QgsStaticExpressionFunction(
9871 u"magnetic_declination_rate_of_change"_s,
9873 << QgsExpressionFunction::Parameter( u"model_name"_s )
9874 << QgsExpressionFunction::Parameter( u"date"_s )
9875 << QgsExpressionFunction::Parameter( u"latitude"_s )
9876 << QgsExpressionFunction::Parameter( u"longitude"_s )
9877 << QgsExpressionFunction::Parameter( u"height"_s )
9878 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9879 fcnMagneticDeclinationRateOfChange,
9880 u"MagneticModels"_s
9881 )
9882 << new QgsStaticExpressionFunction(
9883 u"magnetic_inclination_rate_of_change"_s,
9885 << QgsExpressionFunction::Parameter( u"model_name"_s )
9886 << QgsExpressionFunction::Parameter( u"date"_s )
9887 << QgsExpressionFunction::Parameter( u"latitude"_s )
9888 << QgsExpressionFunction::Parameter( u"longitude"_s )
9889 << QgsExpressionFunction::Parameter( u"height"_s )
9890 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9891 fcnMagneticInclinationRateOfChange,
9892 u"MagneticModels"_s
9893 )
9894
9895 // deprecated stuff - hidden from users
9896 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9897
9898 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9899 geomFunc->setIsStatic( false );
9900 functions << geomFunc;
9901
9902 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9903 areaFunc->setIsStatic( false );
9904 functions << areaFunc;
9905
9906 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9907
9908 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9909 lengthFunc->setIsStatic( false );
9910 functions << lengthFunc;
9911
9912 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9913 perimeterFunc->setIsStatic( false );
9914 functions << perimeterFunc;
9915
9916 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9917
9918 functions << new QgsStaticExpressionFunction( u"roundness"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnRoundness, u"GeometryGroup"_s );
9919
9920 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9921 xFunc->setIsStatic( false );
9922 functions << xFunc;
9923
9924 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9925 yFunc->setIsStatic( false );
9926 functions << yFunc;
9927
9928 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9929 zFunc->setIsStatic( false );
9930 functions << zFunc;
9931
9932 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions {
9933 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9934 { u"overlay_contains"_s, fcnGeomOverlayContains },
9935 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9936 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9937 { u"overlay_equals_exact"_s, fcnGeomOverlayEqualsExact },
9938 { u"overlay_equals_topological"_s, fcnGeomOverlayEqualsTopological },
9939 { u"overlay_equals_fuzzy"_s, fcnGeomOverlayEqualsFuzzy },
9940 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9941 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9942 { u"overlay_within"_s, fcnGeomOverlayWithin },
9943 };
9944 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9945 while ( i.hasNext() )
9946 {
9947 i.next();
9948 QString defaultBackend = i.key() == "overlay_equals"_L1 ? QString( "QGIS" ) : QString( "GEOS" );
9949 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction(
9950 i.key(),
9952 << QgsExpressionFunction::Parameter( u"layer"_s )
9953 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9954 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9955 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9956 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9957 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9958 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9959 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9960 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false )
9961 << QgsExpressionFunction::Parameter( u"backend"_s, true, defaultBackend, false )
9962 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4, false ),
9963 i.value(),
9964 u"GeometryGroup"_s,
9965 QString(),
9966 true,
9967 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9968 true
9969 );
9970
9971 // The current feature is accessed for the geometry, so this should not be cached
9972 fcnGeomOverlayFunc->setIsStatic( false );
9973 functions << fcnGeomOverlayFunc;
9974 }
9975
9976 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction(
9977 u"overlay_nearest"_s,
9979 << QgsExpressionFunction::Parameter( u"layer"_s )
9980 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9981 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9982 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9983 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9984 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9985 fcnGeomOverlayNearest,
9986 u"GeometryGroup"_s,
9987 QString(),
9988 true,
9989 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9990 true
9991 );
9992 // The current feature is accessed for the geometry, so this should not be cached
9993 fcnGeomOverlayNearestFunc->setIsStatic( false );
9994 functions << fcnGeomOverlayNearestFunc;
9995
9996 functions
9997 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9998 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9999 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
10000 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
10001 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
10002 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
10003 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
10004 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
10005 << 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 )
10006 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
10007 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
10008 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
10009 << 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 )
10010 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
10011 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
10012 << new QgsStaticExpressionFunction(
10013 u"make_triangle"_s,
10014 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ) << QgsExpressionFunction::Parameter( u"point3"_s ),
10015 fcnMakeTriangle,
10016 u"GeometryGroup"_s
10017 )
10018 << new QgsStaticExpressionFunction(
10019 u"make_circle"_s,
10021 << QgsExpressionFunction::Parameter( u"center"_s )
10022 << QgsExpressionFunction::Parameter( u"radius"_s )
10023 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
10024 fcnMakeCircle,
10025 u"GeometryGroup"_s
10026 )
10027 << new QgsStaticExpressionFunction(
10028 u"make_ellipse"_s,
10030 << QgsExpressionFunction::Parameter( u"center"_s )
10031 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
10032 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
10033 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10034 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
10035 fcnMakeEllipse,
10036 u"GeometryGroup"_s
10037 )
10038 << new QgsStaticExpressionFunction(
10039 u"make_regular_polygon"_s,
10041 << QgsExpressionFunction::Parameter( u"center"_s )
10042 << QgsExpressionFunction::Parameter( u"radius"_s )
10043 << QgsExpressionFunction::Parameter( u"number_sides"_s )
10044 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
10045 fcnMakeRegularPolygon,
10046 u"GeometryGroup"_s
10047 )
10048 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnMakeSquare, u"GeometryGroup"_s )
10049 << new QgsStaticExpressionFunction(
10050 u"make_rectangle_3points"_s,
10052 << QgsExpressionFunction::Parameter( u"point1"_s )
10053 << QgsExpressionFunction::Parameter( u"point2"_s )
10054 << QgsExpressionFunction::Parameter( u"point3"_s )
10055 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
10056 fcnMakeRectangleFrom3Points,
10057 u"GeometryGroup"_s
10058 )
10059 << new QgsStaticExpressionFunction(
10060 u"make_valid"_s,
10062 QgsExpressionFunction::Parameter( u"geometry"_s ),
10063#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
10064 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
10065#else
10066 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
10067#endif
10068 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
10069 },
10070 fcnGeomMakeValid,
10071 u"GeometryGroup"_s
10072 );
10073
10074 functions
10075 << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
10076 functions
10077 << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
10078 functions
10079 << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
10080 functions
10081 << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
10082
10083 QgsStaticExpressionFunction *xAtFunc
10084 = 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 );
10085 xAtFunc->setIsStatic( false );
10086 functions << xAtFunc;
10087
10088
10089 QgsStaticExpressionFunction *yAtFunc
10090 = 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 );
10091 yAtFunc->setIsStatic( false );
10092 functions << yAtFunc;
10093
10094 functions
10095 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
10096 << 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 )
10097 << 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 )
10098 << 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 )
10099 << 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 )
10100 << 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 )
10101 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10102 << 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 )
10103 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
10104 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
10105 << new QgsStaticExpressionFunction(
10106 u"intersects_bbox"_s,
10107 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10108 fcnBbox,
10109 u"GeometryGroup"_s,
10110 QString(),
10111 false,
10112 QSet<QString>(),
10113 false,
10114 QStringList() << u"bbox"_s
10115 )
10116 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDisjoint, u"GeometryGroup"_s )
10117 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersects, u"GeometryGroup"_s )
10118 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnTouches, u"GeometryGroup"_s )
10119 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCrosses, u"GeometryGroup"_s )
10120 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnContains, u"GeometryGroup"_s )
10121 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnOverlaps, u"GeometryGroup"_s )
10122 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnWithin, u"GeometryGroup"_s )
10123 << new QgsStaticExpressionFunction( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnEquals, u"GeometryGroup"_s )
10124 << new QgsStaticExpressionFunction(
10125 u"equals_exact"_s,
10127 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10128 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10129 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s ),
10130 fcnIsEqualsExact,
10131 u"GeometryGroup"_s
10132 )
10133 << new QgsStaticExpressionFunction(
10134 u"equals_topological"_s,
10136 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10137 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10138 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"GEOS"_s ),
10139 fcnIsEqualsTopological,
10140 u"GeometryGroup"_s
10141 )
10142 << new QgsStaticExpressionFunction(
10143 u"equals_fuzzy"_s,
10145 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10146 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10147 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s )
10148 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4 ),
10149 fcnIsEqualsFuzzy,
10150 u"GeometryGroup"_s
10151 )
10152 << 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 )
10153 << new QgsStaticExpressionFunction(
10154 u"rotate"_s,
10156 << QgsExpressionFunction::Parameter( u"geometry"_s )
10157 << QgsExpressionFunction::Parameter( u"rotation"_s )
10158 << QgsExpressionFunction::Parameter( u"center"_s, true )
10159 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
10160 fcnRotate,
10161 u"GeometryGroup"_s
10162 )
10163 << new QgsStaticExpressionFunction(
10164 u"scale"_s,
10166 << QgsExpressionFunction::Parameter( u"geometry"_s )
10167 << QgsExpressionFunction::Parameter( u"x_scale"_s )
10168 << QgsExpressionFunction::Parameter( u"y_scale"_s )
10169 << QgsExpressionFunction::Parameter( u"center"_s, true ),
10170 fcnScale,
10171 u"GeometryGroup"_s
10172 )
10173 << new QgsStaticExpressionFunction(
10174 u"affine_transform"_s,
10176 << QgsExpressionFunction::Parameter( u"geometry"_s )
10177 << QgsExpressionFunction::Parameter( u"delta_x"_s )
10178 << QgsExpressionFunction::Parameter( u"delta_y"_s )
10179 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
10180 << QgsExpressionFunction::Parameter( u"scale_x"_s )
10181 << QgsExpressionFunction::Parameter( u"scale_y"_s )
10182 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
10183 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
10184 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
10185 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
10186 fcnAffineTransform,
10187 u"GeometryGroup"_s
10188 )
10189 << new QgsStaticExpressionFunction(
10190 u"buffer"_s,
10192 << QgsExpressionFunction::Parameter( u"geometry"_s )
10193 << QgsExpressionFunction::Parameter( u"distance"_s )
10194 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
10195 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
10196 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
10197 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
10198 fcnBuffer,
10199 u"GeometryGroup"_s
10200 )
10201 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForceRHR, u"GeometryGroup"_s )
10202 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCW, u"GeometryGroup"_s )
10203 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCCW, u"GeometryGroup"_s )
10204 << new QgsStaticExpressionFunction(
10205 u"wedge_buffer"_s,
10207 << QgsExpressionFunction::Parameter( u"center"_s )
10208 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10209 << QgsExpressionFunction::Parameter( u"width"_s )
10210 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
10211 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ),
10212 fcnWedgeBuffer,
10213 u"GeometryGroup"_s
10214 )
10215 << new QgsStaticExpressionFunction(
10216 u"tapered_buffer"_s,
10218 << QgsExpressionFunction::Parameter( u"geometry"_s )
10219 << QgsExpressionFunction::Parameter( u"start_width"_s )
10220 << QgsExpressionFunction::Parameter( u"end_width"_s )
10221 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ),
10222 fcnTaperedBuffer,
10223 u"GeometryGroup"_s
10224 )
10225 << 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 )
10226 << new QgsStaticExpressionFunction(
10227 u"offset_curve"_s,
10229 << QgsExpressionFunction::Parameter( u"geometry"_s )
10230 << QgsExpressionFunction::Parameter( u"distance"_s )
10231 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10232 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10233 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10234 fcnOffsetCurve,
10235 u"GeometryGroup"_s
10236 )
10237 << new QgsStaticExpressionFunction(
10238 u"single_sided_buffer"_s,
10240 << QgsExpressionFunction::Parameter( u"geometry"_s )
10241 << QgsExpressionFunction::Parameter( u"distance"_s )
10242 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10243 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10244 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10245 fcnSingleSidedBuffer,
10246 u"GeometryGroup"_s
10247 )
10248 << new QgsStaticExpressionFunction(
10249 u"extend"_s,
10251 << QgsExpressionFunction::Parameter( u"geometry"_s )
10252 << QgsExpressionFunction::Parameter( u"start_distance"_s )
10253 << QgsExpressionFunction::Parameter( u"end_distance"_s )
10254 << QgsExpressionFunction::Parameter( u"start_deflection"_s, true, 0 )
10255 << QgsExpressionFunction::Parameter( u"end_deflection"_s, true, 0 ),
10256 fcnExtend,
10257 u"GeometryGroup"_s
10258 )
10259 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
10260 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
10261 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
10262 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
10263 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
10264 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnInteriorRingN, u"GeometryGroup"_s )
10265 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnGeometryN, u"GeometryGroup"_s )
10266 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
10267 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
10268 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry1"_s ), QgsExpressionFunction::Parameter( u"geometry2"_s ) }, fcnSharedPaths, u"GeometryGroup"_s )
10269 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
10270 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
10271 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
10272 << new QgsStaticExpressionFunction(
10273 u"smooth"_s,
10275 << QgsExpressionFunction::Parameter( u"geometry"_s )
10276 << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
10277 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
10278 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
10279 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ),
10280 fcnSmooth,
10281 u"GeometryGroup"_s
10282 )
10283 << new QgsStaticExpressionFunction(
10284 u"triangular_wave"_s,
10285 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10286 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10287 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10288 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10289 fcnTriangularWave,
10290 u"GeometryGroup"_s
10291 )
10292 << new QgsStaticExpressionFunction(
10293 u"triangular_wave_randomized"_s,
10294 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10295 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10296 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10297 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10298 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10299 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10300 fcnTriangularWaveRandomized,
10301 u"GeometryGroup"_s
10302 )
10303 << new QgsStaticExpressionFunction(
10304 u"square_wave"_s,
10305 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10306 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10307 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10308 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10309 fcnSquareWave,
10310 u"GeometryGroup"_s
10311 )
10312 << new QgsStaticExpressionFunction(
10313 u"square_wave_randomized"_s,
10314 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10315 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10316 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10317 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10318 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10319 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10320 fcnSquareWaveRandomized,
10321 u"GeometryGroup"_s
10322 )
10323 << new QgsStaticExpressionFunction(
10324 u"wave"_s,
10325 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10326 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10327 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10328 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10329 fcnRoundWave,
10330 u"GeometryGroup"_s
10331 )
10332 << new QgsStaticExpressionFunction(
10333 u"wave_randomized"_s,
10334 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10335 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10336 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10337 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10338 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10339 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10340 fcnRoundWaveRandomized,
10341 u"GeometryGroup"_s
10342 )
10343 << new QgsStaticExpressionFunction(
10344 u"apply_dash_pattern"_s,
10345 {
10346 QgsExpressionFunction::Parameter( u"geometry"_s ),
10347 QgsExpressionFunction::Parameter( u"pattern"_s ),
10348 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
10349 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
10350 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
10351 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
10352 },
10353 fcnApplyDashPattern,
10354 u"GeometryGroup"_s
10355 )
10356 << new QgsStaticExpressionFunction( u"densify_by_count"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"vertices"_s ) }, fcnDensifyByCount, u"GeometryGroup"_s )
10357 << new QgsStaticExpressionFunction( u"densify_by_distance"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"distance"_s ) }, fcnDensifyByDistance, u"GeometryGroup"_s )
10358 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
10359 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
10360 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
10361 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
10362 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
10363 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
10364 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
10365 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
10366 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
10367 << new QgsStaticExpressionFunction(
10368 u"is_empty_or_null"_s,
10369 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
10370 fcnIsEmptyOrNull,
10371 u"GeometryGroup"_s,
10372 QString(),
10373 false,
10374 QSet<QString>(),
10375 false,
10376 QStringList(),
10377 true
10378 )
10379 << 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 )
10380#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
10381 << new QgsStaticExpressionFunction(
10382 u"concave_hull"_s,
10384 << QgsExpressionFunction::Parameter( u"geometry"_s )
10385 << QgsExpressionFunction::Parameter( u"target_percent"_s )
10386 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ),
10387 fcnConcaveHull,
10388 u"GeometryGroup"_s
10389 )
10390#endif
10391 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnOrientedBBox, u"GeometryGroup"_s )
10392 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMainAngle, u"GeometryGroup"_s )
10393 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ), fcnMinimalCircle, u"GeometryGroup"_s )
10394 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDifference, u"GeometryGroup"_s )
10395 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDistance, u"GeometryGroup"_s )
10396 << new QgsStaticExpressionFunction(
10397 u"hausdorff_distance"_s,
10399 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10400 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10401 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
10402 fcnHausdorffDistance,
10403 u"GeometryGroup"_s
10404 )
10405 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersection, u"GeometryGroup"_s )
10406 << new QgsStaticExpressionFunction(
10407 u"sym_difference"_s,
10408 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10409 fcnSymDifference,
10410 u"GeometryGroup"_s,
10411 QString(),
10412 false,
10413 QSet<QString>(),
10414 false,
10415 QStringList() << u"symDifference"_s
10416 )
10417 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10418 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10419 << new QgsStaticExpressionFunction(
10420 u"geom_to_wkt"_s,
10421 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
10422 fcnGeomToWKT,
10423 u"GeometryGroup"_s,
10424 QString(),
10425 false,
10426 QSet<QString>(),
10427 false,
10428 QStringList() << u"geomToWKT"_s
10429 )
10430 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10431 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
10432 << new QgsStaticExpressionFunction(
10433 u"transform"_s,
10434 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"source_auth_id"_s ) << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
10435 fcnTransformGeometry,
10436 u"GeometryGroup"_s
10437 )
10438 << new QgsStaticExpressionFunction(
10439 u"extrude"_s,
10440 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ),
10441 fcnExtrude,
10442 u"GeometryGroup"_s,
10443 QString()
10444 )
10445 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsMultipart, u"GeometryGroup"_s )
10446 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMax, u"GeometryGroup"_s )
10447 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMin, u"GeometryGroup"_s )
10448 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMax, u"GeometryGroup"_s )
10449 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMin, u"GeometryGroup"_s )
10450 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSinuosity, u"GeometryGroup"_s )
10451 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStraightDistance2d, u"GeometryGroup"_s );
10452
10453
10454 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction(
10455 u"order_parts"_s,
10457 << QgsExpressionFunction::Parameter( u"geometry"_s )
10458 << QgsExpressionFunction::Parameter( u"orderby"_s )
10459 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
10460 fcnOrderParts,
10461 u"GeometryGroup"_s,
10462 QString()
10463 );
10464
10465 orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10466 const QList< QgsExpressionNode *> argList = node->args()->list();
10467 for ( QgsExpressionNode *argNode : argList )
10468 {
10469 if ( !argNode->isStatic( parent, context ) )
10470 return false;
10471 }
10472
10473 if ( node->args()->count() > 1 )
10474 {
10475 QgsExpressionNode *argNode = node->args()->at( 1 );
10476
10477 QString expString = argNode->eval( parent, context ).toString();
10478
10479 QgsExpression e( expString );
10480
10481 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10482 return true;
10483 }
10484
10485 return true;
10486 } );
10487
10488 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10489 if ( node->args()->count() > 1 )
10490 {
10491 QgsExpressionNode *argNode = node->args()->at( 1 );
10492 QString expression = argNode->eval( parent, context ).toString();
10494 e.prepare( context );
10495 context->setCachedValue( expression, QVariant::fromValue( e ) );
10496 }
10497 return true;
10498 } );
10499 functions << orderPartsFunc;
10500
10501 functions
10502 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnClosestPoint, u"GeometryGroup"_s )
10503 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnShortestLine, u"GeometryGroup"_s )
10504 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
10505 << new QgsStaticExpressionFunction(
10506 u"line_interpolate_point_by_m"_s,
10508 << QgsExpressionFunction::Parameter( u"geometry"_s )
10509 << QgsExpressionFunction::Parameter( u"m"_s )
10510 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10511 fcnLineInterpolatePointByM,
10512 u"GeometryGroup"_s
10513 )
10514 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
10515 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
10516 << new QgsStaticExpressionFunction(
10517 u"line_locate_m"_s,
10519 << QgsExpressionFunction::Parameter( u"geometry"_s )
10520 << QgsExpressionFunction::Parameter( u"m"_s )
10521 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10522 fcnLineLocateM,
10523 u"GeometryGroup"_s
10524 )
10525 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
10526 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
10527 << new QgsStaticExpressionFunction(
10528 u"line_substring"_s,
10529 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10530 fcnLineSubset,
10531 u"GeometryGroup"_s
10532 );
10533
10534
10535 // **Record** functions
10536
10537 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
10538 idFunc->setIsStatic( false );
10539 functions << idFunc;
10540
10541 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
10542 currentFeatureFunc->setIsStatic( false );
10543 functions << currentFeatureFunc;
10544
10545 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction(
10546 u"uuid"_s,
10547 { QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ), QgsExpressionFunction::Parameter( u"version"_s, true, 4 ) },
10548 fcnUuid,
10549 u"Record and Attributes"_s,
10550 QString(),
10551 false,
10552 QSet<QString>(),
10553 false,
10554 QStringList() << u"$uuid"_s
10555 );
10556 uuidFunc->setIsStatic( false );
10557 functions << uuidFunc;
10558
10559 functions
10560 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
10561 << new QgsStaticExpressionFunction(
10562 u"get_feature"_s,
10563 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"value"_s, true ),
10564 fcnGetFeature,
10565 u"Record and Attributes"_s,
10566 QString(),
10567 false,
10568 QSet<QString>(),
10569 false,
10570 QStringList() << u"QgsExpressionUtils::getFeature"_s
10571 )
10572 << new QgsStaticExpressionFunction(
10573 u"get_feature_by_id"_s,
10574 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"feature_id"_s ),
10575 fcnGetFeatureById,
10576 u"Record and Attributes"_s,
10577 QString(),
10578 false,
10579 QSet<QString>(),
10580 false
10581 );
10582
10583 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction(
10584 u"attributes"_s,
10585 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
10586 fcnAttributes,
10587 u"Record and Attributes"_s,
10588 QString(),
10589 false,
10590 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10591 );
10592 attributesFunc->setIsStatic( false );
10593 functions << attributesFunc;
10594 QgsStaticExpressionFunction *representAttributesFunc
10595 = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1, fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10596 representAttributesFunc->setIsStatic( false );
10597 functions << representAttributesFunc;
10598
10599 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction(
10600 u"is_feature_valid"_s,
10602 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10603 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10604 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10605 fcnValidateFeature,
10606 u"Record and Attributes"_s,
10607 QString(),
10608 false,
10609 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10610 );
10611 validateFeature->setIsStatic( false );
10612 functions << validateFeature;
10613
10614 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction(
10615 u"is_attribute_valid"_s,
10617 << QgsExpressionFunction::Parameter( u"attribute"_s, false )
10619 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10620 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10621 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10622 fcnValidateAttribute,
10623 u"Record and Attributes"_s,
10624 QString(),
10625 false,
10626 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10627 );
10628 validateAttribute->setIsStatic( false );
10629 functions << validateAttribute;
10630
10631 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction( u"maptip"_s, -1, fcnFeatureMaptip, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10632 maptipFunc->setIsStatic( false );
10633 functions << maptipFunc;
10634
10635 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction( u"display_expression"_s, -1, fcnFeatureDisplayExpression, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10636 displayFunc->setIsStatic( false );
10637 functions << displayFunc;
10638
10639 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( u"is_selected"_s, -1, fcnIsSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10640 isSelectedFunc->setIsStatic( false );
10641 functions << isSelectedFunc;
10642
10643 functions << new QgsStaticExpressionFunction( u"num_selected"_s, -1, fcnNumSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10644
10645 functions << new QgsStaticExpressionFunction(
10646 u"sqlite_fetch_and_increment"_s,
10648 << QgsExpressionFunction::Parameter( u"database"_s )
10649 << QgsExpressionFunction::Parameter( u"table"_s )
10650 << QgsExpressionFunction::Parameter( u"id_field"_s )
10651 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
10652 << QgsExpressionFunction::Parameter( u"filter_value"_s )
10653 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
10654 fcnSqliteFetchAndIncrement,
10655 u"Record and Attributes"_s
10656 );
10657
10658 // **CRS** functions
10659 functions
10660 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
10661 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
10662
10663
10664 // **Fields and Values** functions
10665 QgsStaticExpressionFunction *representValueFunc
10666 = 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 );
10667
10668 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10669 Q_UNUSED( context )
10670 if ( node->args()->count() == 1 )
10671 {
10672 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
10673 if ( colRef )
10674 {
10675 return true;
10676 }
10677 else
10678 {
10679 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
10680 return false;
10681 }
10682 }
10683 else if ( node->args()->count() == 2 )
10684 {
10685 return true;
10686 }
10687 else
10688 {
10689 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
10690 return false;
10691 }
10692 } );
10693
10694 functions << representValueFunc;
10695
10696 // **General** functions
10697 functions
10698 << new QgsStaticExpressionFunction(
10699 u"layer_property"_s,
10701 << QgsExpressionFunction::Parameter( u"layer"_s )
10702 << QgsExpressionFunction::Parameter( u"property"_s )
10703 << QgsExpressionFunction::Parameter( u"translate"_s, true, true ),
10704 fcnGetLayerProperty,
10705 u"Map Layers"_s
10706 )
10707 << new QgsStaticExpressionFunction( u"decode_uri"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"part"_s, true ), fcnDecodeUri, u"Map Layers"_s )
10708 << new QgsStaticExpressionFunction( u"mime_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary_data"_s ), fcnMimeType, u"General"_s )
10709 << new QgsStaticExpressionFunction(
10710 u"raster_statistic"_s,
10711 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"statistic"_s ),
10712 fcnGetRasterBandStat,
10713 u"Rasters"_s
10714 );
10715
10716 // **var** function
10717 QgsStaticExpressionFunction *varFunction
10718 = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
10719 varFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10720 /* A variable node is static if it has a static name and the name can be found at prepare
10721 * time and is tagged with isStatic.
10722 * It is not static if a variable is set during iteration or not tagged isStatic.
10723 * (e.g. geom_part variable)
10724 */
10725 if ( node->args()->count() > 0 )
10726 {
10727 QgsExpressionNode *argNode = node->args()->at( 0 );
10728
10729 if ( !argNode->isStatic( parent, context ) )
10730 return false;
10731
10732 const QString varName = argNode->eval( parent, context ).toString();
10733 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
10734 return false;
10735
10736 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
10737 return scope ? scope->isStatic( varName ) : false;
10738 }
10739 return false;
10740 } );
10741 varFunction->setUsesGeometryFunction( []( const QgsExpressionNodeFunction *node ) -> bool {
10742 if ( node && node->args()->count() > 0 )
10743 {
10744 QgsExpressionNode *argNode = node->args()->at( 0 );
10745 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
10746 {
10747 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
10748 return true;
10749 }
10750 }
10751 return false;
10752 } );
10753
10754 functions << varFunction;
10755
10756 QgsStaticExpressionFunction *evalTemplateFunction
10757 = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10758 evalTemplateFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10759 if ( node->args()->count() > 0 )
10760 {
10761 QgsExpressionNode *argNode = node->args()->at( 0 );
10762
10763 if ( argNode->isStatic( parent, context ) )
10764 {
10765 QString expString = argNode->eval( parent, context ).toString();
10766
10767 QgsExpression e( expString );
10768
10769 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10770 return true;
10771 }
10772 }
10773
10774 return false;
10775 } );
10776 functions << evalTemplateFunction;
10777
10778 QgsStaticExpressionFunction *evalFunc
10779 = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10780 evalFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10781 if ( node->args()->count() > 0 )
10782 {
10783 QgsExpressionNode *argNode = node->args()->at( 0 );
10784
10785 if ( argNode->isStatic( parent, context ) )
10786 {
10787 QString expString = argNode->eval( parent, context ).toString();
10788
10789 QgsExpression e( expString );
10790
10791 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10792 return true;
10793 }
10794 }
10795
10796 return false;
10797 } );
10798
10799 functions << evalFunc;
10800
10801 QgsStaticExpressionFunction *attributeFunc
10802 = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10803 attributeFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10804 const QList< QgsExpressionNode *> argList = node->args()->list();
10805 for ( QgsExpressionNode *argNode : argList )
10806 {
10807 if ( !argNode->isStatic( parent, context ) )
10808 return false;
10809 }
10810
10811 if ( node->args()->count() == 1 )
10812 {
10813 // not static -- this is the variant which uses the current feature taken direct from the expression context
10814 return false;
10815 }
10816
10817 return true;
10818 } );
10819 functions << attributeFunc;
10820
10821 functions
10822 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10823 << new QgsWithVariableExpressionFunction()
10824 << new QgsStaticExpressionFunction(
10825 u"raster_value"_s,
10826 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10827 fcnRasterValue,
10828 u"Rasters"_s
10829 )
10830 << new QgsStaticExpressionFunction(
10831 u"raster_attributes"_s,
10832 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10833 fcnRasterAttributes,
10834 u"Rasters"_s
10835 )
10836
10837 // functions for arrays
10838 << new QgsArrayForeachExpressionFunction()
10839 << new QgsArrayFilterExpressionFunction()
10840 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10841 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10842 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10843 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10844 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10845 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayAll, u"Arrays"_s )
10846 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10847 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10848 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10849 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10850 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10851 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10852 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10853 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10854 << 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 )
10855 << 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 )
10856 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10857 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10858 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10859 << new QgsStaticExpressionFunction(
10860 u"array_insert"_s,
10861 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10862 fcnArrayInsert,
10863 u"Arrays"_s
10864 )
10865 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10866 << new QgsStaticExpressionFunction(
10867 u"array_remove_all"_s,
10868 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10869 fcnArrayRemoveAll,
10870 u"Arrays"_s,
10871 QString(),
10872 false,
10873 QSet<QString>(),
10874 false,
10875 QStringList(),
10876 true
10877 )
10878 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10879 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"priority"_s ), fcnArrayPrioritize, u"Arrays"_s )
10880 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10881 << new QgsStaticExpressionFunction(
10882 u"array_slice"_s,
10883 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ),
10884 fcnArraySlice,
10885 u"Arrays"_s
10886 )
10887 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10888 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10889 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10890 << new QgsStaticExpressionFunction(
10891 u"array_to_string"_s,
10893 << QgsExpressionFunction::Parameter( u"array"_s )
10894 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10895 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10896 fcnArrayToString,
10897 u"Arrays"_s
10898 )
10899 << new QgsStaticExpressionFunction(
10900 u"string_to_array"_s,
10902 << QgsExpressionFunction::Parameter( u"string"_s )
10903 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10904 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10905 fcnStringToArray,
10906 u"Arrays"_s
10907 )
10908 << new QgsStaticExpressionFunction(
10909 u"generate_series"_s,
10910 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ),
10911 fcnGenerateSeries,
10912 u"Arrays"_s
10913 )
10914 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10915
10916 //functions for maps
10917 << 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 )
10918 << 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 )
10919 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10920 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10921 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10922 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10923 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10924 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10925 << 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 )
10926 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10927 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10928 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10929 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"prefix"_s ), fcnMapPrefixKeys, u"Maps"_s )
10930 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlTable, u"Maps"_s )
10931 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlDefinitionList, u"Maps"_s )
10932 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnToFormUrlEncode, u"Maps"_s )
10933
10934 ;
10935
10937
10938 //QgsExpression has ownership of all built-in functions
10939 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10940 {
10941 *sOwnedFunctions() << func;
10942 *sBuiltinFunctions() << func->name();
10943 sBuiltinFunctions()->append( func->aliases() );
10944 }
10945 }
10946 return functions;
10947}
10948
10949bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10950{
10951 int fnIdx = functionIndex( function->name() );
10952 if ( fnIdx != -1 )
10953 {
10954 return false;
10955 }
10956
10957 QMutexLocker locker( &sFunctionsMutex );
10958 sFunctions()->append( function );
10959 if ( transferOwnership )
10960 sOwnedFunctions()->append( function );
10961
10962 return true;
10963}
10964
10965bool QgsExpression::unregisterFunction( const QString &name )
10966{
10967 // You can never override the built in functions.
10968 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10969 {
10970 return false;
10971 }
10972 int fnIdx = functionIndex( name );
10973 if ( fnIdx != -1 )
10974 {
10975 QMutexLocker locker( &sFunctionsMutex );
10976 sFunctions()->removeAt( fnIdx );
10977 sFunctionIndexMap.clear();
10978 return true;
10979 }
10980 return false;
10981}
10982
10984{
10985 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10986 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10987 {
10988 sBuiltinFunctions()->removeAll( func->name() );
10989 for ( const QString &alias : func->aliases() )
10991 sBuiltinFunctions()->removeAll( alias );
10992 }
10993
10994 sFunctions()->removeAll( func );
10995 }
10996
10997 qDeleteAll( *sOwnedFunctions() );
10998 sOwnedFunctions()->clear();
11000
11001const QStringList &QgsExpression::BuiltinFunctions()
11002{
11003 if ( sBuiltinFunctions()->isEmpty() )
11004 {
11005 Functions(); // this method builds the gmBuiltinFunctions as well
11006 }
11007 return *sBuiltinFunctions();
11008}
11009
11012 u"array_foreach"_s, // skip-keyword-check
11013 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ),
11014 u"Arrays"_s
11015 )
11016{}
11017
11019{
11020 bool isStatic = false;
11021
11022 QgsExpressionNode::NodeList *args = node->args();
11024 if ( args->count() < 2 )
11025 return false;
11026
11027 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11028 {
11029 isStatic = true;
11030 }
11031 return isStatic;
11032}
11033
11035{
11036 Q_UNUSED( node )
11037 QVariantList result;
11038
11039 if ( args->count() < 2 )
11040 // error
11041 return result;
11042
11043 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11044
11045 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11046 std::unique_ptr< QgsExpressionContext > tempContext;
11047 if ( !subContext )
11048 {
11049 tempContext = std::make_unique< QgsExpressionContext >();
11050 subContext = tempContext.get();
11051 }
11052
11053 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11054 subContext->appendScope( subScope );
11055
11056 int i = 0;
11057 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
11058 {
11059 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
11060 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
11061 result << args->at( 1 )->eval( parent, subContext );
11062 }
11063
11064 if ( context )
11065 delete subContext->popScope();
11066
11067 return result;
11068}
11069
11070QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11072 // This is a dummy function, all the real handling is in run
11073 Q_UNUSED( values )
11074 Q_UNUSED( context )
11075 Q_UNUSED( parent )
11076 Q_UNUSED( node )
11077
11078 Q_ASSERT( false );
11079 return QVariant();
11080}
11081
11083{
11084 QgsExpressionNode::NodeList *args = node->args();
11085
11086 if ( args->count() < 2 )
11087 // error
11088 return false;
11089
11090 args->at( 0 )->prepare( parent, context );
11091
11092 QgsExpressionContext subContext;
11093 if ( context )
11094 subContext = *context;
11097 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11098 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
11099 subContext.appendScope( subScope );
11100
11101 args->at( 1 )->prepare( parent, &subContext );
11102
11103 return true;
11104}
11105
11107 : 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 )
11108{}
11109
11111{
11112 bool isStatic = false;
11113
11114 QgsExpressionNode::NodeList *args = node->args();
11116 if ( args->count() < 2 )
11117 return false;
11118
11119 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11120 {
11121 isStatic = true;
11122 }
11123 return isStatic;
11124}
11125
11127{
11128 Q_UNUSED( node )
11129 QVariantList result;
11130
11131 if ( args->count() < 2 )
11132 // error
11133 return result;
11134
11135 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11136
11137 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11138 std::unique_ptr< QgsExpressionContext > tempContext;
11139 if ( !subContext )
11140 {
11141 tempContext = std::make_unique< QgsExpressionContext >();
11142 subContext = tempContext.get();
11143 }
11144
11145 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11146 subContext->appendScope( subScope );
11147
11148 int limit = 0;
11149 if ( args->count() >= 3 )
11150 {
11151 const QVariant limitVar = args->at( 2 )->eval( parent, context );
11152
11153 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
11154 {
11155 limit = limitVar.toInt();
11156 }
11157 else
11158 {
11159 return result;
11160 }
11161 }
11162
11163 for ( const QVariant &value : array )
11164 {
11165 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
11166 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
11167 {
11168 result << value;
11169
11170 if ( limit > 0 && limit == result.size() )
11171 break;
11172 }
11173 }
11174
11175 if ( context )
11176 delete subContext->popScope();
11177
11178 return result;
11179}
11180
11181QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11183 // This is a dummy function, all the real handling is in run
11184 Q_UNUSED( values )
11185 Q_UNUSED( context )
11186 Q_UNUSED( parent )
11187 Q_UNUSED( node )
11188
11189 Q_ASSERT( false );
11190 return QVariant();
11191}
11192
11194{
11195 QgsExpressionNode::NodeList *args = node->args();
11196
11197 if ( args->count() < 2 )
11198 // error
11199 return false;
11200
11201 args->at( 0 )->prepare( parent, context );
11202
11203 QgsExpressionContext subContext;
11204 if ( context )
11205 subContext = *context;
11206
11208 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11209 subContext.appendScope( subScope );
11210
11211 args->at( 1 )->prepare( parent, &subContext );
11212
11213 return true;
11214}
11216 : 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 )
11217{}
11218
11220{
11221 bool isStatic = false;
11222
11223 QgsExpressionNode::NodeList *args = node->args();
11224
11225 if ( args->count() < 3 )
11226 return false;
11227
11228 // We only need to check if the node evaluation is static, if both - name and value - are static.
11229 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11230 {
11231 QVariant name = args->at( 0 )->eval( parent, context );
11232 QVariant value = args->at( 1 )->eval( parent, context );
11234 // Temporarily append a new scope to provide the variable
11235 appendTemporaryVariable( context, name.toString(), value );
11236 if ( args->at( 2 )->isStatic( parent, context ) )
11237 isStatic = true;
11238 popTemporaryVariable( context );
11239 }
11240
11241 return isStatic;
11242}
11243
11245{
11246 Q_UNUSED( node )
11247 QVariant result;
11248
11249 if ( args->count() < 3 )
11250 // error
11251 return result;
11252
11253 QVariant name = args->at( 0 )->eval( parent, context );
11254 QVariant value = args->at( 1 )->eval( parent, context );
11255
11256 const QgsExpressionContext *updatedContext = context;
11257 std::unique_ptr< QgsExpressionContext > tempContext;
11258 if ( !updatedContext )
11259 {
11260 tempContext = std::make_unique< QgsExpressionContext >();
11261 updatedContext = tempContext.get();
11263
11264 appendTemporaryVariable( updatedContext, name.toString(), value );
11265 result = args->at( 2 )->eval( parent, updatedContext );
11266
11267 if ( context )
11268 popTemporaryVariable( updatedContext );
11269
11270 return result;
11271}
11272
11273QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11275 // This is a dummy function, all the real handling is in run
11276 Q_UNUSED( values )
11277 Q_UNUSED( context )
11278 Q_UNUSED( parent )
11279 Q_UNUSED( node )
11280
11281 Q_ASSERT( false );
11282 return QVariant();
11283}
11284
11286{
11287 QgsExpressionNode::NodeList *args = node->args();
11288
11289 if ( args->count() < 3 )
11290 // error
11291 return false;
11292
11293 QVariant name = args->at( 0 )->prepare( parent, context );
11294 QVariant value = args->at( 1 )->prepare( parent, context );
11295
11296 const QgsExpressionContext *updatedContext = context;
11297 std::unique_ptr< QgsExpressionContext > tempContext;
11298 if ( !updatedContext )
11299 {
11300 tempContext = std::make_unique< QgsExpressionContext >();
11301 updatedContext = tempContext.get();
11302 }
11303
11304 appendTemporaryVariable( updatedContext, name.toString(), value );
11305 args->at( 2 )->prepare( parent, updatedContext );
11306
11307 if ( context )
11308 popTemporaryVariable( updatedContext );
11309
11310 return true;
11311}
11312
11313void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
11314{
11315 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11316 delete updatedContext->popScope();
11317}
11318
11319void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
11320{
11321 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
11322 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
11323
11324 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11325 updatedContext->appendScope( scope );
11326}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2319
@ GEOS
Use GEOS implementation.
Definition qgis.h:2321
@ QGIS
Use internal implementation.
Definition qgis.h:2320
@ Left
Buffer to left of line.
Definition qgis.h:2269
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3529
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3531
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3530
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3532
@ Success
Operation succeeded.
Definition qgis.h:2213
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3241
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2380
@ Point
Points.
Definition qgis.h:400
@ Line
Lines.
Definition qgis.h:401
@ Polygon
Polygons.
Definition qgis.h:402
@ Unknown
Unknown types.
Definition qgis.h:403
@ Null
No geometry.
Definition qgis.h:404
JoinStyle
Join styles for buffers.
Definition qgis.h:2293
@ Bevel
Use beveled joins.
Definition qgis.h:2296
@ Round
Use rounded joins.
Definition qgis.h:2294
@ Miter
Use mitered joins.
Definition qgis.h:2295
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6761
@ StdDev
Standard deviation.
Definition qgis.h:6768
@ NoStatistic
No statistic.
Definition qgis.h:6762
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:214
@ Plugin
Plugin based layer.
Definition qgis.h:209
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2280
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2282
@ Round
Round cap.
Definition qgis.h:2281
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2283
Aggregate
Available aggregates to calculate.
Definition qgis.h:6638
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6655
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6652
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6645
@ Median
Median of values (numeric fields only).
Definition qgis.h:6646
@ Max
Max of values.
Definition qgis.h:6643
@ Min
Min of values.
Definition qgis.h:6642
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6656
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6649
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6660
@ Sum
Sum of values.
Definition qgis.h:6644
@ Minority
Minority of values.
Definition qgis.h:6650
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6641
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6659
@ Majority
Majority of values.
Definition qgis.h:6651
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6648
@ Count
Count.
Definition qgis.h:6639
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6653
@ CountDistinct
Number of distinct values.
Definition qgis.h:6640
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6657
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6658
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6654
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3514
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3517
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3519
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3518
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3516
@ NoRule
No special rule.
Definition qgis.h:3515
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2364
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2365
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2366
@ Point
Point.
Definition qgis.h:316
@ PointM
PointM.
Definition qgis.h:349
@ PointZ
PointZ.
Definition qgis.h:333
@ GeometryCollection
GeometryCollection.
Definition qgis.h:323
@ PointZM
PointZM.
Definition qgis.h:365
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:46
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:282
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:277
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:41
Defines a QGIS exception class.
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
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:45
int count
Definition qgsfields.h:49
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Encapsulates parameters under which a geometry operation is performed.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points shared by this geometry and other.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false, QgsFeedback *feedback=nullptr) const
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
Q_INVOKABLE bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
bool isExactlyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
Q_INVOKABLE bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry extendLine(double startDistance, double endDistance, double startDeflection=0, double endDeflection=0) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
Q_INVOKABLE bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
Q_INVOKABLE bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false, QgsFeedback *feedback=nullptr) const
Attempts to make an invalid geometry valid without losing vertices.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
QgsGeometry buffer(double distance, int segments, QgsFeedback *feedback=nullptr) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
bool isFuzzyEqual(const QgsGeometry &geometry, double epsilon=1e-4, Qgis::GeometryBackend backend=Qgis::GeometryBackend::QGIS) const
Compares the geometry with another geometry within the tolerance epsilon using the specified backend.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
bool isTopologicallyEqual(const QgsGeometry &geometry, Qgis::GeometryBackend backend=Qgis::GeometryBackend::GEOS) const
Compares the geometry with another geometry using the specified backend.
QgsGeometry simplify(double tolerance, QgsFeedback *feedback=nullptr) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Q_INVOKABLE bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
Q_INVOKABLE bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:178
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:3403
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
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:114
QgsRelationManager * relationManager
Definition qgsproject.h:125
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
bool isRamp() const
Returns true if the field carries a color ramp component information (RedMin/RedMax,...
bool isColor() const
Returns true if the field carries a color component (Red, Green, Blue and optionally Alpha) informati...
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
void grow(double delta)
Grows the rectangle in place by the specified amount.
double yMaximum
QgsPointXY center
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices).
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
Q_INVOKABLE QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString unaccent(const QString &input)
Removes accents and other diacritical marks from a string, replacing accented characters with their u...
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance).
Definition qgsstyle.cpp:543
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:164
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
Decodes a string to a color.
static QString encodeColor(const QColor &color)
Lossy-encodes a color to a string.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:596
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:8018
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8361
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8360
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7819
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7725
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