QGIS API Documentation 4.3.0-Master (6402a64e93b)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
6 email : matthias@opengis.ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
45#include "qgsmagneticmodel.h"
47#include "qgsmessagelog.h"
48#include "qgsmultilinestring.h"
49#include "qgsmultipoint.h"
50#include "qgsogcutils.h"
51#include "qgspolygon.h"
52#include "qgsproviderregistry.h"
53#include "qgsquadrilateral.h"
54#include "qgsrasterbandstats.h"
55#include "qgsrasterlayer.h"
56#include "qgsregularpolygon.h"
57#include "qgsspatialindex.h"
58#include "qgsstringutils.h"
59#include "qgsstyle.h"
60#include "qgssymbollayerutils.h"
61#include "qgsthreadingutils.h"
62#include "qgstransaction.h"
63#include "qgstriangle.h"
64#include "qgsunittypes.h"
65#include "qgsvariantutils.h"
66#include "qgsvectorlayer.h"
68#include "qgsvectorlayerutils.h"
69
70#include <QCryptographicHash>
71#include <QMimeDatabase>
72#include <QProcessEnvironment>
73#include <QRegularExpression>
74#include <QString>
75#include <QUrlQuery>
76#include <QUuid>
77
78using namespace Qt::StringLiterals;
79
80typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
81
83Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
85
88Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
89
90const QString QgsExpressionFunction::helpText() const
91{
92 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
93}
94
96{
97 Q_UNUSED( node )
98 // evaluate arguments
99 QVariantList argValues;
100 if ( args )
101 {
102 int arg = 0;
103 const QList< QgsExpressionNode * > argList = args->list();
104 argValues.reserve( argList.size() );
105 for ( QgsExpressionNode *n : argList )
106 {
107 QVariant v;
108 if ( lazyEval() )
109 {
110 // Pass in the node for the function to eval as it needs.
111 v = QVariant::fromValue( n );
112 }
113 else
114 {
115 v = n->eval( parent, context );
117 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
118 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
119 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
120 }
121 argValues.append( v );
122 arg++;
123 }
124 }
125
126 return func( argValues, context, parent, node );
127}
128
130{
131 Q_UNUSED( node )
132 return true;
133}
134
136{
137 return QStringList();
138}
139
141{
142 Q_UNUSED( parent )
143 Q_UNUSED( context )
144 Q_UNUSED( node )
145 return false;
146}
147
149{
150 Q_UNUSED( parent )
151 Q_UNUSED( context )
152 Q_UNUSED( node )
153 return true;
154}
155
157{
158 Q_UNUSED( node )
159 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
160}
161
163{
164 return mGroups.isEmpty() ? false : mGroups.contains( u"deprecated"_s );
165}
166
168{
169 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
170}
171
173{
174 return mHandlesNull;
175}
176
177// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
180 const QString &fnname,
182 FcnEval fcn,
183 const QString &group,
184 const QString &helpText,
185 const std::function< bool( const QgsExpressionNodeFunction *node ) > &usesGeometry,
186 const std::function< QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
187 bool lazyEval,
188 const QStringList &aliases,
189 bool handlesNull
190)
191 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
192 , mFnc( fcn )
193 , mAliases( aliases )
194 , mUsesGeometry( false )
195 , mUsesGeometryFunc( usesGeometry )
196 , mReferencedColumnsFunc( referencedColumns )
197{}
199
201{
202 return mAliases;
203}
204
206{
207 if ( mUsesGeometryFunc )
208 return mUsesGeometryFunc( node );
209 else
210 return mUsesGeometry;
211}
212
214{
215 mUsesGeometryFunc = usesGeometry;
216}
217
219{
220 if ( mReferencedColumnsFunc )
221 return mReferencedColumnsFunc( node );
222 else
223 return mReferencedColumns;
224}
225
227{
228 if ( mIsStaticFunc )
229 return mIsStaticFunc( node, parent, context );
230 else
231 return mIsStatic;
232}
233
235{
236 if ( mPrepareFunc )
237 return mPrepareFunc( node, parent, context );
238
239 return true;
240}
241
243{
244 mIsStaticFunc = isStatic;
245}
246
248{
249 mIsStaticFunc = nullptr;
250 mIsStatic = isStatic;
251}
252
253void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
254{
255 mPrepareFunc = prepareFunc;
256}
257
259{
260 if ( node && node->args() )
261 {
262 const QList< QgsExpressionNode * > argList = node->args()->list();
263 for ( QgsExpressionNode *argNode : argList )
264 {
265 if ( !argNode->isStatic( parent, context ) )
266 return false;
267 }
268 }
269
270 return true;
271}
272
273static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
274{
275 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
276 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
277 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
278
279 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
280 return QVariant();
281
282 QVariantList array;
283 int length = 1;
284
285 array << start;
286 double current = start + step;
287 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
288 {
289 array << current;
290 current += step;
291 length++;
292 }
293
294 return array;
295}
296
297static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
298{
299 if ( !context )
300 return QVariant();
301
302 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
303 if ( context->hasGeometry() )
304 return context->geometry();
305 else
306 {
307 FEAT_FROM_CONTEXT( context, f )
308 QgsGeometry geom = f.geometry();
309 if ( !geom.isNull() )
310 return QVariant::fromValue( geom );
311 else
312 return QVariant();
313 }
314}
315
316static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
317{
318 if ( !context )
319 return QVariant();
320
321 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
322
323 if ( name == "feature"_L1 )
324 {
325 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
326 }
327 else if ( name == "id"_L1 )
328 {
329 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
330 }
331 else if ( name == "geometry"_L1 )
332 {
333 return fcnGeometry( values, context, parent, node );
334 }
335 else
336 {
337 return context->variable( name );
338 }
339}
340
341static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
342{
343 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
344 return QgsExpression::replaceExpressionText( templateString, context );
345}
346
347static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
348{
349 if ( !context )
350 return QVariant();
351
352 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
353 QgsExpression expression( expString );
354 return expression.evaluate( context );
355}
356
357static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
358{
359 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
360 return QVariant( std::sqrt( x ) );
361}
362
363static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
364{
365 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
366 return QVariant( std::fabs( val ) );
367}
368
369static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
370{
371 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
372 return ( deg * M_PI ) / 180;
373}
374static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
375{
376 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
377 return ( 180 * rad ) / M_PI;
378}
379static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
380{
381 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
382 return QVariant( std::sin( x ) );
383}
384static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
385{
386 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
387 return QVariant( std::cos( x ) );
388}
389static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
390{
391 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
392 return QVariant( std::tan( x ) );
393}
394static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
395{
396 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
397 return QVariant( std::asin( x ) );
398}
399static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
400{
401 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
402 return QVariant( std::acos( x ) );
403}
404static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 return QVariant( std::atan( x ) );
408}
409static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
410{
411 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
412 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
413 return QVariant( std::atan2( y, x ) );
414}
415static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
416{
417 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
418 return QVariant( std::exp( x ) );
419}
420static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
421{
422 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
423 if ( x <= 0 )
424 return QVariant();
425 return QVariant( std::log( x ) );
426}
427static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
428{
429 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
430 if ( x <= 0 )
431 return QVariant();
432 return QVariant( log10( x ) );
433}
434static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
435{
436 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
437 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
438 if ( x <= 0 || b <= 0 )
439 return QVariant();
440 return QVariant( std::log( x ) / std::log( b ) );
441}
442static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
443{
444 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
445 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
446 if ( max < min )
447 return QVariant();
448
449 std::random_device rd;
450 std::mt19937_64 generator( rd() );
451
452 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
453 {
454 quint32 seed;
455 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
456 {
457 // if seed can be converted to int, we use as is
458 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
459 }
460 else
461 {
462 // if not, we hash string representation to int
463 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
464 std::hash<std::string> hasher;
465 seed = hasher( seedStr.toStdString() );
466 }
467 generator.seed( seed );
468 }
469
470 // Return a random double in the range [min, max] (inclusive)
471 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
472 return QVariant( min + f * ( max - min ) );
473}
474static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
475{
476 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
477 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
478 if ( max < min )
479 return QVariant();
480
481 std::random_device rd;
482 std::mt19937_64 generator( rd() );
483
484 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
485 {
486 quint32 seed;
487 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
488 {
489 // if seed can be converted to int, we use as is
490 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
491 }
492 else
493 {
494 // if not, we hash string representation to int
495 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
496 std::hash<std::string> hasher;
497 seed = hasher( seedStr.toStdString() );
498 }
499 generator.seed( seed );
500 }
501
502 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
503 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
504 return QVariant( randomInteger );
505
506 // Prevent wrong conversion of QVariant. See #36412
507 return QVariant( int( randomInteger ) );
508}
509
510static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
511{
512 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
513 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
514 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
515 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
516 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
517
518 if ( domainMin >= domainMax )
519 {
520 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
521 return QVariant();
522 }
523
524 // outside of domain?
525 if ( val >= domainMax )
526 {
527 return rangeMax;
528 }
529 else if ( val <= domainMin )
530 {
531 return rangeMin;
532 }
533
534 // calculate linear scale
535 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
536 double c = rangeMin - ( domainMin * m );
537
538 // Return linearly scaled value
539 return QVariant( m * val + c );
540}
541
542static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
543{
544 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
545 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
546 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
547 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
548 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
549 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
550
551 if ( domainMin >= domainMax )
552 {
553 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
554 return QVariant();
555 }
556 if ( exponent <= 0 )
557 {
558 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
559 return QVariant();
560 }
561
562 // outside of domain?
563 if ( val >= domainMax )
564 {
565 return rangeMax;
566 }
567 else if ( val <= domainMin )
568 {
569 return rangeMin;
570 }
571
572 // Return polynomially scaled value
573 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
574}
575
576static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
577{
578 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
579 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
580 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
581 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
582 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
583 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
584
585 if ( domainMin >= domainMax )
586 {
587 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
588 return QVariant();
589 }
590 if ( exponent <= 0 )
591 {
592 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
593 return QVariant();
594 }
595
596 // outside of domain?
597 if ( val >= domainMax )
598 {
599 return rangeMax;
600 }
601 else if ( val <= domainMin )
602 {
603 return rangeMin;
604 }
605
606 // Return exponentially scaled value
607 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
608 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
609}
610
611static QVariant fcnCubicBezierScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
612{
613 const double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
614 const double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
615 const double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
616 const double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
617 const double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
618
619 const double x1 = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
620 const double y1 = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
621 const double x2 = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
622 const double y2 = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
623
624 if ( x1 < 0.0 || x1 > 1.0 || y1 < 0.0 || y1 > 1.0 || x2 < 0.0 || x2 > 1.0 || y2 < 0.0 || y2 > 1.0 )
625 {
626 parent->setEvalErrorString( QObject::tr( "Cubic bezier control points must be between 0 and 1" ) );
627 return QVariant();
628 }
629
630 if ( domainMin >= domainMax )
631 {
632 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
633 return QVariant();
634 }
635
636 // outside of domain?
637 if ( val >= domainMax )
638 {
639 return rangeMax;
640 }
641 else if ( val <= domainMin )
642 {
643 return rangeMin;
644 }
645
646 // normalize input to [0, 1] range
647 const double t = ( val - domainMin ) / ( domainMax - domainMin );
648
649 // solve using UnitBezier approach (based on MapLibre native's implementation)
650 const double cx = 3.0 * x1;
651 const double bx = 3.0 * ( x2 - x1 ) - cx;
652 const double ax = 1.0 - cx - bx;
653 const double cy = 3.0 * y1;
654 const double by = 3.0 * ( y2 - y1 ) - cy;
655 const double ay = 1.0 - cy - by;
656
657 constexpr double epsilon = 1e-6;
658
659 // solve for s using Newton's method (8 iterations)
660 double s = t;
661 bool solved = false;
662 for ( int i = 0; i < 8; ++i )
663 {
664 const double x2val = ( ( ax * s + bx ) * s + cx ) * s - t;
665 if ( std::fabs( x2val ) < epsilon )
666 {
667 solved = true;
668 break;
669 }
670 const double d2 = ( 3.0 * ax * s + 2.0 * bx ) * s + cx;
671 if ( std::fabs( d2 ) < 1e-6 )
672 break;
673 s = s - x2val / d2;
674 }
675
676 if ( !solved )
677 {
678 // fallback to bisection approach
679 double t0 = 0.0;
680 double t1 = 1.0;
681 s = t;
682
683 if ( s < t0 )
684 {
685 s = t0;
686 solved = true;
687 }
688 else if ( s > t1 )
689 {
690 s = t1;
691 solved = true;
692 }
693
694 while ( !solved && t0 < t1 )
695 {
696 const double x2val = ( ( ax * s + bx ) * s + cx ) * s;
697 if ( std::fabs( x2val - t ) < epsilon )
698 {
699 solved = true;
700 break;
701 }
702 if ( t > x2val )
703 t0 = s;
704 else
705 t1 = s;
706 s = ( t1 - t0 ) * 0.5 + t0;
707 }
708 }
709
710 const double easedT = ( ( ay * s + by ) * s + cy ) * s;
711 return QVariant( ( rangeMax - rangeMin ) * easedT + rangeMin );
712}
713
714static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
715{
716 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
717 double maxVal = std::numeric_limits<double>::quiet_NaN();
718 for ( const QVariant &val : values )
719 {
720 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
721 if ( std::isnan( maxVal ) )
722 {
723 maxVal = testVal;
724 }
725 else if ( !std::isnan( testVal ) )
726 {
727 maxVal = std::max( maxVal, testVal );
728 }
729 }
730
731 if ( !std::isnan( maxVal ) )
732 {
733 result = QVariant( maxVal );
734 }
735 return result;
736}
737
738static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
739{
740 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
741 double minVal = std::numeric_limits<double>::quiet_NaN();
742 for ( const QVariant &val : values )
743 {
744 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
745 if ( std::isnan( minVal ) )
746 {
747 minVal = testVal;
748 }
749 else if ( !std::isnan( testVal ) )
750 {
751 minVal = std::min( minVal, testVal );
752 }
753 }
754
755 if ( !std::isnan( minVal ) )
756 {
757 result = QVariant( minVal );
758 }
759 return result;
760}
761
762static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
763{
764 //lazy eval, so we need to evaluate nodes now
765
766 //first node is layer id or name
767 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
769 QVariant value = node->eval( parent, context );
771
772 // TODO this expression function is NOT thread safe
774 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
776 if ( !vl )
777 {
778 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
779 return QVariant();
780 }
781
782 // second node is aggregate type
783 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
785 value = node->eval( parent, context );
787 bool ok = false;
788 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
789 if ( !ok )
790 {
791 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
792 return QVariant();
793 }
794
795 // third node is subexpression (or field name)
796 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
798 QString subExpression = node->dump();
799
801 //optional forth node is filter
802 if ( values.count() > 3 )
803 {
804 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
806 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
807 if ( !nl || nl->value().isValid() )
808 parameters.filter = node->dump();
809 }
810
811 //optional fifth node is concatenator
812 if ( values.count() > 4 )
813 {
814 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
816 value = node->eval( parent, context );
818 parameters.delimiter = value.toString();
819 }
820
821 //optional sixth node is order by
822 QString orderBy;
823 if ( values.count() > 5 )
824 {
825 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
827 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
828 if ( !nl || nl->value().isValid() )
829 {
830 orderBy = node->dump();
831 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
832 }
833 }
834
835 QString aggregateError;
836 QVariant result;
837 if ( context )
838 {
839 QString cacheKey;
840 QgsExpression subExp( subExpression );
841 QgsExpression filterExp( parameters.filter );
842
843 const QSet< QString > filterVars = filterExp.referencedVariables();
844 const QSet< QString > subExpVars = subExp.referencedVariables();
845 QSet<QString> allVars = filterVars + subExpVars;
846
847 bool isStatic = true;
848 if ( filterVars.contains( u"parent"_s ) || filterVars.contains( QString() ) || subExpVars.contains( u"parent"_s ) || subExpVars.contains( QString() ) )
849 {
850 isStatic = false;
851 }
852 else
853 {
854 for ( const QString &varName : allVars )
855 {
856 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
857 if ( scope && !scope->isStatic( varName ) )
858 {
859 isStatic = false;
860 break;
861 }
862 }
863 }
864
865 if ( isStatic && !parameters.orderBy.isEmpty() )
866 {
867 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
868 {
869 const QgsExpression &orderByExpression { orderByClause.expression() };
870 if ( orderByExpression.referencedVariables().contains( u"parent"_s ) || orderByExpression.referencedVariables().contains( QString() ) )
871 {
872 isStatic = false;
873 break;
874 }
875 }
876 }
877
878 if ( !isStatic )
879 {
880 bool ok = false;
881 const QString contextHash = context->uniqueHash( ok, allVars );
882 if ( ok )
883 {
884 cacheKey = u"aggfcn:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
885 }
886 }
887 else
888 {
889 cacheKey = u"aggfcn:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
890 }
891
892 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
893 {
894 return context->cachedValue( cacheKey );
895 }
896
897 QgsExpressionContext subContext( *context );
899 subScope->setVariable( u"parent"_s, context->feature(), true );
900 subContext.appendScope( subScope );
901 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
902
903 if ( ok && !cacheKey.isEmpty() )
904 {
905 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
906 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
907 // associated with it's calculation!
908 context->setCachedValue( cacheKey, result );
909 }
910 }
911 else
912 {
913 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
914 }
915 if ( !ok )
916 {
917 if ( !aggregateError.isEmpty() )
918 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
919 else
920 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
921 return QVariant();
922 }
923
924 return result;
925}
926
927static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
928{
929 if ( !context )
930 {
931 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
932 return QVariant();
933 }
934
935 // first step - find current layer
936
937 // TODO this expression function is NOT thread safe
939 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
941 if ( !vl )
942 {
943 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
944 return QVariant();
945 }
946
947 //lazy eval, so we need to evaluate nodes now
948
949 //first node is relation name
950 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
952 QVariant value = node->eval( parent, context );
954 QString relationId = value.toString();
955 // check relation exists
956 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
957 if ( !relation.isValid() || relation.referencedLayer() != vl )
958 {
959 // check for relations by name
960 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
961 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
962 {
963 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
964 return QVariant();
965 }
966 else
967 {
968 relation = relations.at( 0 );
969 }
970 }
971
972 QgsVectorLayer *childLayer = relation.referencingLayer();
973
974 // second node is aggregate type
975 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
977 value = node->eval( parent, context );
979 bool ok = false;
980 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
981 if ( !ok )
982 {
983 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
984 return QVariant();
985 }
986
987 //third node is subexpression (or field name)
988 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
990 QString subExpression = node->dump();
991
992 //optional fourth node is concatenator
994 if ( values.count() > 3 )
995 {
996 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
998 value = node->eval( parent, context );
1000 parameters.delimiter = value.toString();
1001 }
1002
1003 //optional fifth node is order by
1004 QString orderBy;
1005 if ( values.count() > 4 )
1006 {
1007 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
1009 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1010 if ( !nl || nl->value().isValid() )
1011 {
1012 orderBy = node->dump();
1013 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1014 }
1015 }
1016
1017 if ( !context->hasFeature() )
1018 return QVariant();
1019 QgsFeature f = context->feature();
1020
1021 parameters.filter = relation.getRelatedFeaturesFilter( f );
1022
1023 const QString cacheKey = u"relagg:%1%:%2:%3:%4:%5:%6"_s.arg( relationId, vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1024 if ( context->hasCachedValue( cacheKey ) )
1025 return context->cachedValue( cacheKey );
1026
1027 QVariant result;
1028 ok = false;
1029
1030
1031 QgsExpressionContext subContext( *context );
1032 QString error;
1033 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1034
1035 if ( !ok )
1036 {
1037 if ( !error.isEmpty() )
1038 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1039 else
1040 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1041 return QVariant();
1042 }
1043
1044 // cache value
1045 context->setCachedValue( cacheKey, result );
1046 return result;
1047}
1048
1049
1050static QVariant fcnAggregateGeneric(
1051 Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1
1052)
1053{
1054 if ( !context )
1055 {
1056 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1057 return QVariant();
1058 }
1059
1060 // first step - find current layer
1061
1062 // TODO this expression function is NOT thread safe
1064 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
1066 if ( !vl )
1067 {
1068 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
1069 return QVariant();
1070 }
1071
1072 //lazy eval, so we need to evaluate nodes now
1073
1074 //first node is subexpression (or field name)
1075 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
1077 QString subExpression = node->dump();
1078
1079 //optional second node is group by
1080 QString groupBy;
1081 if ( values.count() > 1 )
1082 {
1083 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
1085 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1086 if ( !nl || nl->value().isValid() )
1087 groupBy = node->dump();
1088 }
1089
1090 //optional third node is filter
1091 if ( values.count() > 2 )
1092 {
1093 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
1095 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1096 if ( !nl || nl->value().isValid() )
1097 parameters.filter = node->dump();
1098 }
1099
1100 //optional order by node, if supported
1101 QString orderBy;
1102 if ( orderByPos >= 0 && values.count() > orderByPos )
1103 {
1104 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
1106 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
1107 if ( !nl || nl->value().isValid() )
1108 {
1109 orderBy = node->dump();
1110 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
1111 }
1112 }
1113
1114 // build up filter with group by
1115
1116 // find current group by value
1117 if ( !groupBy.isEmpty() )
1118 {
1119 QgsExpression groupByExp( groupBy );
1120 QVariant groupByValue = groupByExp.evaluate( context );
1121 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy, QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s, QgsExpression::quotedValue( groupByValue ) );
1122 if ( !parameters.filter.isEmpty() )
1123 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1124 else
1125 parameters.filter = groupByClause;
1126 }
1127
1128 QgsExpression subExp( subExpression );
1129 QgsExpression filterExp( parameters.filter );
1130
1131 bool isStatic = true;
1132 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1133 for ( const QString &varName : refVars )
1134 {
1135 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1136 if ( scope && !scope->isStatic( varName ) )
1137 {
1138 isStatic = false;
1139 break;
1140 }
1141 }
1142
1143 QString cacheKey;
1144 if ( !isStatic )
1145 {
1146 bool ok = false;
1147 const QString contextHash = context->uniqueHash( ok, refVars );
1148 if ( ok )
1149 {
1150 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
1151 }
1152 }
1153 else
1154 {
1155 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1156 }
1157
1158 if ( context->hasCachedValue( cacheKey ) )
1159 return context->cachedValue( cacheKey );
1160
1161 QVariant result;
1162 bool ok = false;
1163
1164 QgsExpressionContext subContext( *context );
1166 subScope->setVariable( u"parent"_s, context->feature(), true );
1167 subContext.appendScope( subScope );
1168 QString error;
1169 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1170
1171 if ( !ok )
1172 {
1173 if ( !error.isEmpty() )
1174 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1175 else
1176 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1177 return QVariant();
1178 }
1179
1180 // cache value
1181 context->setCachedValue( cacheKey, result );
1182 return result;
1183}
1184
1185
1186static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1187{
1188 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1189}
1190
1191static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1192{
1193 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1194}
1195
1196static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1197{
1198 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1199}
1200
1201static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1202{
1203 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1204}
1205
1206static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1207{
1208 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1209}
1210
1211static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1212{
1213 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1214}
1215
1216static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1217{
1218 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1219}
1220
1221static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1222{
1223 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1224}
1225
1226static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1227{
1228 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1229}
1230
1231static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1232{
1233 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1234}
1235
1236static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1237{
1238 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1239}
1240
1241static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1244}
1245
1246static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1247{
1248 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1249}
1250
1251static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1252{
1253 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1254}
1255
1256static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1257{
1258 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1259}
1260
1261static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1262{
1263 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1264}
1265
1266static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1267{
1268 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1269}
1270
1271static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1272{
1273 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1274}
1275
1276static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1277{
1279
1280 //fourth node is concatenator
1281 if ( values.count() > 3 )
1282 {
1283 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1285 QVariant value = node->eval( parent, context );
1287 parameters.delimiter = value.toString();
1288 }
1289
1290 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1291}
1292
1293static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1294{
1296
1297 //fourth node is concatenator
1298 if ( values.count() > 3 )
1299 {
1300 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1302 QVariant value = node->eval( parent, context );
1304 parameters.delimiter = value.toString();
1305 }
1306
1307 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1308}
1309
1310static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1311{
1312 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1313}
1314
1315static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1316{
1317 if ( !context )
1318 return QVariant();
1319
1320 QVariant scale = context->variable( u"map_scale"_s );
1321 bool ok = false;
1322 if ( QgsVariantUtils::isNull( scale ) )
1323 return QVariant();
1324
1325 const double v = scale.toDouble( &ok );
1326 if ( ok )
1327 return v;
1328 return QVariant();
1329}
1330
1331static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1332{
1333 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1334 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1335 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1336
1337 // force testValue to sit inside the range specified by the min and max value
1338 if ( testValue <= minValue )
1339 {
1340 return QVariant( minValue );
1341 }
1342 else if ( testValue >= maxValue )
1343 {
1344 return QVariant( maxValue );
1345 }
1346 else
1347 {
1348 return QVariant( testValue );
1349 }
1350}
1351
1352static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1353{
1354 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1355 return QVariant( std::floor( x ) );
1356}
1357
1358static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1359{
1360 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1361 return QVariant( std::ceil( x ) );
1362}
1363
1364static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1365{
1366 const QVariant value = values.at( 0 );
1367 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1368 {
1369 return QVariant( false );
1370 }
1371 else if ( value.userType() == QMetaType::QString )
1372 {
1373 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1374 return QVariant( !value.toString().isEmpty() );
1375 }
1376 else if ( QgsExpressionUtils::isList( value ) )
1377 {
1378 return !value.toList().isEmpty();
1379 }
1380 return QVariant( value.toBool() );
1381}
1382static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1383{
1384 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1385}
1386static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1387{
1388 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1389}
1390static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1391{
1392 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1393}
1394
1395static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1396{
1397 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1398 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1399 if ( format.isEmpty() && !language.isEmpty() )
1400 {
1401 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1402 return QVariant( QDateTime() );
1403 }
1404
1405 if ( format.isEmpty() && language.isEmpty() )
1406 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1407
1408 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409 QLocale locale = QLocale();
1410 if ( !language.isEmpty() )
1411 {
1412 locale = QLocale( language );
1413 }
1414
1415 QDateTime datetime = locale.toDateTime( datetimestring, format );
1416 if ( !datetime.isValid() )
1417 {
1418 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1419 datetime = QDateTime();
1420 }
1421 return QVariant( datetime );
1422}
1423
1424static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1425{
1426 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1427 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1428 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1429
1430 const QDate date( year, month, day );
1431 if ( !date.isValid() )
1432 {
1433 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1434 return QVariant();
1435 }
1436 return QVariant( date );
1437}
1438
1439static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1440{
1441 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1442 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1443 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1444
1445 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1446 if ( !time.isValid() )
1447 {
1448 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1449 return QVariant();
1450 }
1451 return QVariant( time );
1452}
1453
1454static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1455{
1456 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1457 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1458 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1459 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1460 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1461 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1462
1463 const QDate date( year, month, day );
1464 if ( !date.isValid() )
1465 {
1466 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1467 return QVariant();
1468 }
1469 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1470 if ( !time.isValid() )
1471 {
1472 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1473 return QVariant();
1474 }
1475 return QVariant( QDateTime( date, time ) );
1476}
1477
1478static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1479{
1480 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1481
1482 QTimeZone tz;
1483
1484#if QT_FEATURE_timezone > 0
1485 if ( !timeZoneId.isEmpty() )
1486 {
1487 tz = QTimeZone( timeZoneId.toUtf8() );
1488 }
1489
1490 if ( !tz.isValid() )
1491 {
1492 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1493 return QVariant();
1494 }
1495
1496#else
1497 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1498#endif
1499 return QVariant::fromValue( tz );
1500}
1501
1502static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1503{
1504#if QT_FEATURE_timezone > 0
1505 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1506 if ( datetime.isValid() )
1507 {
1508 return QVariant::fromValue( datetime.timeZone() );
1509 }
1510 return QVariant();
1511#else
1512 Q_UNUSED( values )
1513 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1514 return QVariant();
1515#endif
1516}
1517
1518static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1519{
1520#if QT_FEATURE_timezone > 0
1521 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1522 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1523 if ( datetime.isValid() && tz.isValid() )
1524 {
1525 datetime.setTimeZone( tz );
1526 return QVariant::fromValue( datetime );
1527 }
1528 return QVariant();
1529#else
1530 Q_UNUSED( values )
1531 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1532 return QVariant();
1533#endif
1534}
1535
1536static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1537{
1538#if QT_FEATURE_timezone > 0
1539 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1540 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1541 if ( datetime.isValid() && tz.isValid() )
1542 {
1543 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1544 }
1545 return QVariant();
1546#else
1547 Q_UNUSED( values )
1548 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1549 return QVariant();
1550#endif
1551}
1552
1553static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1554{
1555#if QT_FEATURE_timezone > 0
1556 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1557 if ( timeZone.isValid() )
1558 {
1559 return QString( timeZone.id() );
1560 }
1561 return QVariant();
1562#else
1563 Q_UNUSED( values )
1564 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1565 return QVariant();
1566#endif
1567}
1568
1569static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1570{
1571 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1572 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1573 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1574 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1575 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1576 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1577 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1578
1579 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1580}
1581
1582static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1583{
1584 for ( const QVariant &value : values )
1585 {
1586 if ( QgsVariantUtils::isNull( value ) )
1587 continue;
1588 return value;
1589 }
1590 return QVariant();
1591}
1592
1593static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1594{
1595 const QVariant val1 = values.at( 0 );
1596 const QVariant val2 = values.at( 1 );
1597
1598 if ( val1 == val2 )
1599 return QVariant();
1600 else
1601 return val1;
1602}
1603
1604static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1605{
1606 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1607 return QVariant( str.toLower() );
1608}
1609
1610static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1611{
1612 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1613 return QVariant( str.toUpper() );
1614}
1615
1616static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1617{
1618 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1619 QStringList elems = str.split( ' ' );
1620 for ( int i = 0; i < elems.size(); i++ )
1621 {
1622 if ( elems[i].size() > 1 )
1623 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1624 }
1625 return QVariant( elems.join( ' '_L1 ) );
1626}
1627
1628static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1629{
1630 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1631 return QVariant( str.trimmed() );
1632}
1633
1634static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1635{
1636 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1637
1638 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1639
1640 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1641 str.replace( re, QString() );
1642 return QVariant( str );
1643}
1644
1645static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1646{
1647 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1648
1649 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1650
1651 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1652 str.replace( re, QString() );
1653 return QVariant( str );
1654}
1655
1656static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1657{
1658 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1659 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1660 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1661}
1662
1663static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1664{
1665 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1666 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1667 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1668}
1669
1670static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1671{
1672 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1673 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1674 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1675 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1676}
1677
1678static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1679{
1680 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1681 return QVariant( QgsStringUtils::soundex( string ) );
1682}
1683
1684static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1685{
1686 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1687 return QVariant( QString( character ) );
1688}
1689
1690static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1691{
1692 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1693
1694 if ( value.isEmpty() )
1695 {
1696 return QVariant();
1697 }
1698
1699 int res = value.at( 0 ).unicode();
1700 return QVariant( res );
1701}
1702
1703static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1704{
1705 if ( values.length() == 2 || values.length() == 3 )
1706 {
1707 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1708 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1709
1710 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1711
1712 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1713 }
1714
1715 return QVariant();
1716}
1717
1718static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1719{
1720 // two variants, one for geometry, one for string
1721
1722 //geometry variant
1723 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1724 if ( !geom.isNull() )
1725 {
1726 if ( geom.type() == Qgis::GeometryType::Line )
1727 return QVariant( geom.length() );
1728 else
1729 return QVariant();
1730 }
1731
1732 //otherwise fall back to string variant
1733 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1734 return QVariant( str.length() );
1735}
1736
1737static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1738{
1739 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1740
1741 if ( geom.type() != Qgis::GeometryType::Line )
1742 return QVariant();
1743
1744 double totalLength = 0;
1745 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1746 {
1748 {
1749 totalLength += line->length3D();
1750 }
1751 else
1752 {
1753 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1754 totalLength += segmentized->length3D();
1755 }
1756 }
1757
1758 return totalLength;
1759}
1760
1761
1762static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1763{
1764 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1765 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1766 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1767}
1768
1769static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1770{
1771 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1772 {
1773 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1774 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1775 QVector< QPair< QString, QString > > mapItems;
1776
1777 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1778 {
1779 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1780 }
1781
1782 // larger keys should be replaced first since they may contain whole smaller keys
1783 std::sort( mapItems.begin(), mapItems.end(), []( const QPair< QString, QString > &pair1, const QPair< QString, QString > &pair2 ) { return ( pair1.first.length() > pair2.first.length() ); } );
1784
1785 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1786 {
1787 str = str.replace( it->first, it->second );
1788 }
1789
1790 return QVariant( str );
1791 }
1792 else if ( values.count() == 3 )
1793 {
1794 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1795 QVariantList before;
1796 QVariantList after;
1797 bool isSingleReplacement = false;
1798
1799 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1800 {
1801 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1802 }
1803 else
1804 {
1805 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1806 }
1807
1808 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1809 {
1810 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1811 isSingleReplacement = true;
1812 }
1813 else
1814 {
1815 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1816 }
1817
1818 if ( !isSingleReplacement && before.length() != after.length() )
1819 {
1820 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1821 return QVariant();
1822 }
1823
1824 for ( int i = 0; i < before.length(); i++ )
1825 {
1826 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1827 }
1828
1829 return QVariant( str );
1830 }
1831 else
1832 {
1833 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1834 return QVariant();
1835 }
1836}
1837
1838static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1839{
1840 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1841 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1842 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1843
1844 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1845 if ( !re.isValid() )
1846 {
1847 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1848 return QVariant();
1849 }
1850 return QVariant( str.replace( re, after ) );
1851}
1852
1853static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1854{
1855 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1856 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1857
1858 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1859 if ( !re.isValid() )
1860 {
1861 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1862 return QVariant();
1863 }
1864 return QVariant( ( str.indexOf( re ) + 1 ) );
1865}
1866
1867static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1868{
1869 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1870 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1871 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1872
1873 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1874 if ( !re.isValid() )
1875 {
1876 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1877 return QVariant();
1878 }
1879
1880 QRegularExpressionMatch matches = re.match( str );
1881 if ( matches.hasMatch() )
1882 {
1883 QVariantList array;
1884 QStringList list = matches.capturedTexts();
1885
1886 // Skip the first string to only return captured groups
1887 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1888 {
1889 array += ( !( *it ).isEmpty() ) ? *it : empty;
1890 }
1891
1892 return QVariant( array );
1893 }
1894 else
1895 {
1896 return QVariant();
1897 }
1898}
1899
1900static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1901{
1902 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1903 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1904
1905 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1906 if ( !re.isValid() )
1907 {
1908 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1909 return QVariant();
1910 }
1911
1912 // extract substring
1913 QRegularExpressionMatch match = re.match( str );
1914 if ( match.hasMatch() )
1915 {
1916 // return first capture
1917 if ( match.lastCapturedIndex() > 0 )
1918 {
1919 // a capture group was present, so use that
1920 return QVariant( match.captured( 1 ) );
1921 }
1922 else
1923 {
1924 // no capture group, so using all match
1925 return QVariant( match.captured( 0 ) );
1926 }
1927 }
1928 else
1929 {
1930 return QVariant( "" );
1931 }
1932}
1933
1934static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1935{
1936 const int version = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1937 QUuid uuid;
1938 switch ( version )
1939 {
1940 case 4:
1941 uuid = QUuid::createUuid();
1942 break;
1943 case 7:
1944#if QT_VERSION >= QT_VERSION_CHECK( 6, 9, 0 )
1945 uuid = QUuid::createUuidV7();
1946#else
1947 parent->setEvalErrorString( QObject::tr( "UUid version 7 is not supported on this QGIS build" ) );
1948 return QVariant();
1949#endif
1950 break;
1951 default:
1952 parent->setEvalErrorString( QObject::tr( "UUid version %1 is not supported" ).arg( version ) );
1953 return QVariant();
1954 }
1955
1956 const QString format = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1957 if ( format.compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1958 return uuid.toString( QUuid::StringFormat::WithoutBraces );
1959 else if ( format.compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1960 return uuid.toString( QUuid::StringFormat::Id128 );
1961 return uuid.toString();
1962}
1963
1964static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1965{
1966 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1967 return QVariant();
1968
1969 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1970 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1971
1972 int len = 0;
1973 if ( values.at( 2 ).isValid() )
1974 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1975 else
1976 len = str.size();
1977
1978 if ( from < 0 )
1979 {
1980 from = str.size() + from;
1981 if ( from < 0 )
1982 {
1983 from = 0;
1984 }
1985 }
1986 else if ( from > 0 )
1987 {
1988 //account for the fact that substr() starts at 1
1989 from -= 1;
1990 }
1991
1992 if ( len < 0 )
1993 {
1994 len = str.size() + len - from;
1995 if ( len < 0 )
1996 {
1997 len = 0;
1998 }
1999 }
2000
2001 return QVariant( str.mid( from, len ) );
2002}
2003static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2004{
2005 FEAT_FROM_CONTEXT( context, f )
2006 return QVariant( f.id() );
2007}
2008
2009static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2010{
2011 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2012 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
2013 bool foundLayer = false;
2014 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2015 values.at( 0 ),
2016 context,
2017 parent,
2018 [parent, bandNb, geom]( QgsMapLayer *mapLayer ) {
2019 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
2020 if ( !layer || !layer->dataProvider() )
2021 {
2022 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2023 return QVariant();
2024 }
2025
2026 if ( bandNb < 1 || bandNb > layer->bandCount() )
2027 {
2028 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
2029 return QVariant();
2030 }
2031
2032 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
2033 {
2034 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
2035 return QVariant();
2036 }
2037
2038 QgsPointXY point = geom.asPoint();
2039 if ( geom.isMultipart() )
2040 {
2041 QgsMultiPointXY multiPoint = geom.asMultiPoint();
2042 if ( multiPoint.count() == 1 )
2043 {
2044 point = multiPoint[0];
2045 }
2046 else
2047 {
2048 // if the geometry contains more than one part, return an undefined value
2049 return QVariant();
2050 }
2051 }
2052
2053 double value = layer->dataProvider()->sample( point, bandNb );
2054 return std::isnan( value ) ? QVariant() : value;
2055 },
2056 foundLayer
2057 );
2058
2059 if ( !foundLayer )
2060 {
2061 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
2062 return QVariant();
2063 }
2064 else
2065 {
2066 return res;
2067 }
2068}
2069
2070static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2071{
2072 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2073 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2074
2075 bool foundLayer = false;
2076 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2077 values.at( 0 ),
2078 context,
2079 parent,
2080 [parent, bandNb, value]( QgsMapLayer *mapLayer ) -> QVariant {
2081 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
2082 if ( !layer || !layer->dataProvider() )
2083 {
2084 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2085 return QVariant();
2086 }
2087
2088 if ( bandNb < 1 || bandNb > layer->bandCount() )
2089 {
2090 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
2091 return QVariant();
2092 }
2093
2094 if ( std::isnan( value ) )
2095 {
2096 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
2097 return QVariant();
2098 }
2099
2100 if ( !layer->dataProvider()->attributeTable( bandNb ) )
2101 {
2102 return QVariant();
2103 }
2104
2105 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
2106 if ( data.isEmpty() )
2107 {
2108 return QVariant();
2109 }
2110
2111 QVariantMap result;
2112 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
2113 for ( int idx = 0; idx < static_cast<int>( fields.count() ) && idx < static_cast<int>( data.count() ); ++idx )
2114 {
2115 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
2116 if ( field.isColor() || field.isRamp() )
2117 {
2118 continue;
2119 }
2120 result.insert( fields.at( idx ).name, data.at( idx ) );
2121 }
2122
2123 return result;
2124 },
2125 foundLayer
2126 );
2127
2128 if ( !foundLayer )
2129 {
2130 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
2131 return QVariant();
2132 }
2133 else
2134 {
2135 return res;
2136 }
2137}
2138
2139static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2140{
2141 if ( !context )
2142 return QVariant();
2143
2144 return context->feature();
2145}
2146
2147static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2148{
2149 QgsFeature feature;
2150 QString attr;
2151 if ( values.size() == 1 )
2152 {
2153 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2154 feature = context->feature();
2155 }
2156 else if ( values.size() == 2 )
2157 {
2158 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2159 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2160 }
2161 else
2162 {
2163 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2164 return QVariant();
2165 }
2166
2167 return feature.attribute( attr );
2168}
2169
2170static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2171{
2172 QString table { R"html(
2173 <table>
2174 <thead>
2175 <tr><th>%1</th></tr>
2176 </thead>
2177 <tbody>
2178 <tr><td>%2</td></tr>
2179 </tbody>
2180 </table>)html" };
2181 QVariantMap dict;
2182 if ( values.size() == 1 )
2183 {
2184 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2185 }
2186 else
2187 {
2188 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2189 return QVariant();
2190 }
2191
2192 if ( dict.isEmpty() )
2193 {
2194 return QVariant();
2195 }
2196
2197 QStringList headers;
2198 QStringList cells;
2199
2200 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2201 {
2202 headers.push_back( it.key().toHtmlEscaped() );
2203 cells.push_back( it.value().toString().toHtmlEscaped() );
2204 }
2205
2206 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2207}
2208
2209static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2210{
2211 QString table { R"html(
2212 <dl>
2213 %1
2214 </dl>)html" };
2215 QVariantMap dict;
2216 if ( values.size() == 1 )
2217 {
2218 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2219 }
2220 else
2221 {
2222 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2223 return QVariant();
2224 }
2225
2226 if ( dict.isEmpty() )
2227 {
2228 return QVariant();
2229 }
2230
2231 QString rows;
2232
2233 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2234 {
2235 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2236 }
2237
2238 return table.arg( rows );
2239}
2240
2241static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2242{
2243 QVariant layer;
2244 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2245 {
2246 layer = context->variable( u"layer"_s );
2247 }
2248 else
2249 {
2250 //first node is layer id or name
2251 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2253 layer = node->eval( parent, context );
2255 }
2256
2257 QgsFeature feature;
2258 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2259 {
2260 feature = context->feature();
2261 }
2262 else
2263 {
2264 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2265 }
2266
2268 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2269 if ( strength == "hard"_L1 )
2270 {
2272 }
2273 else if ( strength == "soft"_L1 )
2274 {
2276 }
2277
2278 bool foundLayer = false;
2279 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2280 layer,
2281 context,
2282 parent,
2283 [parent, feature, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2284 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2285 if ( !layer )
2286 {
2287 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2288 return QVariant();
2289 }
2290
2291 const QgsFields fields = layer->fields();
2292 bool valid = true;
2293 for ( int i = 0; i < fields.size(); i++ )
2294 {
2295 QStringList errors;
2296 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2297 if ( !valid )
2298 {
2299 break;
2300 }
2301 }
2302
2303 return valid;
2304 },
2305 foundLayer
2306 );
2307
2308 if ( !foundLayer )
2309 {
2310 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2311 return QVariant();
2312 }
2313
2314 return res;
2315}
2316
2317static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2318{
2319 QVariant layer;
2320 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2321 {
2322 layer = context->variable( u"layer"_s );
2323 }
2324 else
2325 {
2326 //first node is layer id or name
2327 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2329 layer = node->eval( parent, context );
2331 }
2332
2333 QgsFeature feature;
2334 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2335 {
2336 feature = context->feature();
2337 }
2338 else
2339 {
2340 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2341 }
2342
2344 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2345 if ( strength == "hard"_L1 )
2346 {
2348 }
2349 else if ( strength == "soft"_L1 )
2350 {
2352 }
2353
2354 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2355
2356 bool foundLayer = false;
2357 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2358 layer,
2359 context,
2360 parent,
2361 [parent, feature, attributeName, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2362 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2363 if ( !layer )
2364 {
2365 return QVariant();
2366 }
2367
2368 const int fieldIndex = layer->fields().indexFromName( attributeName );
2369 if ( fieldIndex == -1 )
2370 {
2371 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2372 return QVariant();
2373 }
2374
2375 QStringList errors;
2376 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2377 return valid;
2378 },
2379 foundLayer
2380 );
2381
2382 if ( !foundLayer )
2383 {
2384 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2385 return QVariant();
2386 }
2387
2388 return res;
2389}
2390
2391static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2392{
2393 QgsFeature feature;
2394 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2395 {
2396 feature = context->feature();
2397 }
2398 else
2399 {
2400 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2401 }
2402
2403 const QgsFields fields = feature.fields();
2404 QVariantMap result;
2405 for ( int i = 0; i < fields.count(); ++i )
2406 {
2407 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2408 }
2409 return result;
2410}
2411
2412static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2413{
2414 QgsVectorLayer *layer = nullptr;
2415 QgsFeature feature;
2416
2417 // TODO this expression function is NOT thread safe
2419 if ( values.isEmpty() )
2420 {
2421 feature = context->feature();
2422 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2423 }
2424 else if ( values.size() == 1 )
2425 {
2426 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2427 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2428 }
2429 else if ( values.size() == 2 )
2430 {
2431 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2432 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2433 }
2434 else
2435 {
2436 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2437 return QVariant();
2438 }
2440
2441 if ( !layer )
2442 {
2443 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2444 return QVariant();
2445 }
2446
2447 if ( !feature.isValid() )
2448 {
2449 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2450 return QVariant();
2451 }
2452
2453 const QgsFields fields = feature.fields();
2454 QVariantMap result;
2455 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2456 {
2457 const QString fieldName { fields.at( fieldIndex ).name() };
2458 const QVariant attributeVal = feature.attribute( fieldIndex );
2459 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2460 if ( context && context->hasCachedValue( cacheValueKey ) )
2461 {
2462 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2463 }
2464 else
2465 {
2466 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2468 QVariant cache;
2469 if ( context )
2470 {
2471 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2472
2473 if ( !context->hasCachedValue( cacheKey ) )
2474 {
2475 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2476 context->setCachedValue( cacheKey, cache );
2477 }
2478 else
2479 {
2480 cache = context->cachedValue( cacheKey );
2481 }
2482 }
2483 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2484
2485 result.insert( fields.at( fieldIndex ).name(), value );
2486
2487 if ( context )
2488 {
2489 context->setCachedValue( cacheValueKey, value );
2490 }
2491 }
2492 }
2493 return result;
2494}
2495
2496static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2497{
2498 QgsVectorLayer *layer = nullptr;
2499 QgsFeature feature;
2500 bool evaluate = true;
2501
2502 // TODO this expression function is NOT thread safe
2504 if ( values.isEmpty() )
2505 {
2506 feature = context->feature();
2507 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2508 }
2509 else if ( values.size() == 1 )
2510 {
2511 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2512 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2513 }
2514 else if ( values.size() == 2 )
2515 {
2516 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2517 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2518 }
2519 else if ( values.size() == 3 )
2520 {
2521 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2522 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2523 evaluate = values.value( 2 ).toBool();
2524 }
2525 else
2526 {
2527 if ( isMaptip )
2528 {
2529 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2530 }
2531 else
2532 {
2533 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2534 }
2535 return QVariant();
2536 }
2537
2538 if ( !layer )
2539 {
2540 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2541 return QVariant();
2542 }
2544
2545 if ( !feature.isValid() )
2546 {
2547 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2548 return QVariant();
2549 }
2550
2551 if ( !evaluate )
2552 {
2553 if ( isMaptip )
2554 {
2555 return layer->mapTipTemplate();
2556 }
2557 else
2558 {
2559 return layer->displayExpression();
2560 }
2561 }
2562
2563 QgsExpressionContext subContext( *context );
2564 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2565 subContext.setFeature( feature );
2566
2567 if ( isMaptip )
2568 {
2569 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2570 }
2571 else
2572 {
2573 QgsExpression exp( layer->displayExpression() );
2574 exp.prepare( &subContext );
2575 return exp.evaluate( &subContext ).toString();
2576 }
2577}
2578
2579static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2580{
2581 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2582}
2583
2584static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2585{
2586 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2587}
2588
2589static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2590{
2591 QgsFeature feature;
2592 QVariant layer;
2593 if ( values.isEmpty() )
2594 {
2595 feature = context->feature();
2596 layer = context->variable( u"layer"_s );
2597 }
2598 else if ( values.size() == 1 )
2599 {
2600 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2601 layer = context->variable( u"layer"_s );
2602 }
2603 else if ( values.size() == 2 )
2604 {
2605 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2606 layer = values.at( 0 );
2607 }
2608 else
2609 {
2610 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2611 return QVariant();
2612 }
2613
2614 bool foundLayer = false;
2615 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2616 layer,
2617 context,
2618 parent,
2619 [feature]( QgsMapLayer *mapLayer ) -> QVariant {
2620 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2621 if ( !layer || !feature.isValid() )
2622 {
2623 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2624 }
2625
2626 return layer->selectedFeatureIds().contains( feature.id() );
2627 },
2628 foundLayer
2629 );
2630 if ( !foundLayer )
2631 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2632 else
2633 return res;
2634}
2635
2636static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2637{
2638 QVariant layer;
2639
2640 if ( values.isEmpty() )
2641 layer = context->variable( u"layer"_s );
2642 else if ( values.count() == 1 )
2643 layer = values.at( 0 );
2644 else
2645 {
2646 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2647 return QVariant();
2648 }
2649
2650 bool foundLayer = false;
2651 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2652 layer,
2653 context,
2654 parent,
2655 []( QgsMapLayer *mapLayer ) -> QVariant {
2656 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2657 if ( !layer )
2658 {
2659 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2660 }
2661
2662 return layer->selectedFeatureCount();
2663 },
2664 foundLayer
2665 );
2666 if ( !foundLayer )
2667 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2668 else
2669 return res;
2670}
2671
2672static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2673{
2674 static QMap<QString, qlonglong> counterCache;
2675 QVariant functionResult;
2676
2677 auto fetchAndIncrementFunc = [values, parent, &functionResult]( QgsMapLayer *mapLayer, const QString &databaseArgument ) {
2678 QString database;
2679
2680 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2681
2682 if ( layer )
2683 {
2684 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2685 database = decodedUri.value( u"path"_s ).toString();
2686 if ( database.isEmpty() )
2687 {
2688 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2689 }
2690 }
2691 else
2692 {
2693 database = databaseArgument;
2694 }
2695
2696 const QString table = values.at( 1 ).toString();
2697 const QString idColumn = values.at( 2 ).toString();
2698 const QString filterAttribute = values.at( 3 ).toString();
2699 const QVariant filterValue = values.at( 4 ).toString();
2700 const QVariantMap defaultValues = values.at( 5 ).toMap();
2701
2702 // read from database
2704 sqlite3_statement_unique_ptr sqliteStatement;
2705
2706 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2707 {
2708 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2709 functionResult = QVariant();
2710 return;
2711 }
2712
2713 QString errorMessage;
2714 QString currentValSql;
2715
2716 qlonglong nextId = 0;
2717 bool cachedMode = false;
2718 bool valueRetrieved = false;
2719
2720 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2721
2722 // Running in transaction mode, check for cached value first
2723 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2724 {
2725 cachedMode = true;
2726
2727 auto cachedCounter = counterCache.find( cacheString );
2728
2729 if ( cachedCounter != counterCache.end() )
2730 {
2731 qlonglong &cachedValue = cachedCounter.value();
2732 nextId = cachedValue;
2733 nextId += 1;
2734 cachedValue = nextId;
2735 valueRetrieved = true;
2736 }
2737 }
2738
2739 // Either not in cached mode or no cached value found, obtain from DB
2740 if ( !cachedMode || !valueRetrieved )
2741 {
2742 int result = SQLITE_ERROR;
2743
2744 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2745 if ( !filterAttribute.isNull() )
2746 {
2747 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2748 }
2749
2750 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2751
2752 if ( result == SQLITE_OK )
2753 {
2754 nextId = 0;
2755 if ( sqliteStatement.step() == SQLITE_ROW )
2756 {
2757 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2758 }
2759
2760 // If in cached mode: add value to cache and connect to transaction
2761 if ( cachedMode && result == SQLITE_OK )
2762 {
2763 counterCache.insert( cacheString, nextId );
2764
2765 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() { counterCache.remove( cacheString ); } );
2766 }
2767 valueRetrieved = true;
2768 }
2769 }
2770
2771 if ( valueRetrieved )
2772 {
2773 QString upsertSql;
2774 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2775 QStringList cols;
2776 QStringList vals;
2777 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2778 vals << QgsSqliteUtils::quotedValue( nextId );
2779
2780 if ( !filterAttribute.isNull() )
2781 {
2782 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2783 vals << QgsSqliteUtils::quotedValue( filterValue );
2784 }
2785
2786 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2787 {
2788 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2789 vals << iter.value().toString();
2790 }
2791
2792 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2793 upsertSql += " VALUES "_L1;
2794 upsertSql += '(' + vals.join( ',' ) + ')';
2795
2796 int result = SQLITE_ERROR;
2797 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2798 {
2799 QgsTransaction *transaction = layer->dataProvider()->transaction();
2800 if ( transaction->executeSql( upsertSql, errorMessage ) )
2801 {
2802 result = SQLITE_OK;
2803 }
2804 }
2805 else
2806 {
2807 result = sqliteDb.exec( upsertSql, errorMessage );
2808 }
2809 if ( result == SQLITE_OK )
2810 {
2811 functionResult = QVariant( nextId );
2812 return;
2813 }
2814 else
2815 {
2816 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2817 functionResult = QVariant();
2818 return;
2819 }
2820 }
2821
2822 functionResult = QVariant();
2823 };
2824
2825 bool foundLayer = false;
2826 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer *layer ) { fetchAndIncrementFunc( layer, QString() ); }, foundLayer );
2827 if ( !foundLayer )
2828 {
2829 const QString databasePath = values.at( 0 ).toString();
2830 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath] { fetchAndIncrementFunc( nullptr, databasePath ); } );
2831 }
2832
2833 return functionResult;
2834}
2835
2836static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2837{
2838 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2839 if ( !crs.isValid() )
2840 return QVariant();
2841 return crs.authid();
2842}
2843
2844static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2845{
2846 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2847 QgsCoordinateReferenceSystem crs( definition );
2848
2849 if ( !crs.isValid() )
2850 {
2851 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2852 }
2853
2854 return QVariant::fromValue( crs );
2855}
2856
2857static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2858{
2859 QString concat( "" );
2860 for ( const QVariant &value : values )
2861 {
2862 if ( !QgsVariantUtils::isNull( value ) )
2863 concat += QgsExpressionUtils::getStringValue( value, parent );
2864 }
2865 return concat;
2866}
2867
2868static QVariant fcnConcatWs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2869{
2870 if ( values.length() < 2 )
2871 {
2872 parent->setEvalErrorString( QObject::tr( "Function concat_ws requires at least 2 arguments" ) );
2873 return QVariant();
2874 }
2875
2876 const QString separator = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2877
2878 QStringList stringValues;
2879 stringValues.reserve( values.size() - 1 );
2880 for ( int i = 1; i < values.size(); ++i )
2881 {
2882 const QVariant value = values.at( i );
2883 if ( !QgsVariantUtils::isNull( value ) )
2884 {
2885 stringValues.append( QgsExpressionUtils::getStringValue( value, parent ) );
2886 }
2887 }
2888
2889 return stringValues.join( separator );
2890}
2891
2892static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2893{
2894 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2895 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2896}
2897
2898static QVariant fcnUnaccent( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction *node )
2899{
2900 Q_UNUSED( context )
2901 Q_UNUSED( node )
2902
2903 if ( values.isEmpty() || values[0].isNull() )
2904 return QVariant();
2905
2906 return QgsStringUtils::unaccent( values[0].toString() );
2907}
2908
2909static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2910{
2911 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2912 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2913 return string.right( pos );
2914}
2915
2916static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2917{
2918 if ( values.length() < 2 || values.length() > 3 )
2919 return QVariant();
2920
2921 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2922 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2923
2924 bool overlapping = false;
2925 if ( values.length() == 3 )
2926 {
2927 overlapping = values.at( 2 ).toBool();
2928 }
2929
2930 if ( substring.isEmpty() )
2931 return QVariant( 0 );
2932
2933 int count = 0;
2934 if ( overlapping )
2935 {
2936 count = input.count( substring );
2937 }
2938 else
2939 {
2940 int pos = 0;
2941 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2942 {
2943 count++;
2944 pos += substring.length();
2945 }
2946 }
2947
2948 return QVariant( count );
2949}
2950
2951static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2952{
2953 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2954 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2955 return string.left( pos );
2956}
2957
2958static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2959{
2960 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2961 const int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2962 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2963 if ( fill.isEmpty() )
2964 {
2965 fill = u" "_s;
2966 }
2967 return string.leftJustified( length, fill.at( 0 ), true );
2968}
2969
2970static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2971{
2972 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2973 const int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2974 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2975 if ( fill.isEmpty() )
2976 {
2977 fill = u" "_s;
2978 }
2979 return string.rightJustified( length, fill.at( 0 ), true );
2980}
2981
2982static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2983{
2984 if ( values.size() < 1 )
2985 {
2986 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2987 return QVariant();
2988 }
2989
2990 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2991 for ( int n = 1; n < values.length(); n++ )
2992 {
2993 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2994 }
2995 return string;
2996}
2997
2998
2999static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3000{
3001 return QVariant( QDateTime::currentDateTime() );
3002}
3003
3004static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3005{
3006 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3007 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3008 if ( format.isEmpty() && !language.isEmpty() )
3009 {
3010 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
3011 return QVariant( QDate() );
3012 }
3013
3014 if ( format.isEmpty() && language.isEmpty() )
3015 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
3016
3017 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3018 QLocale locale = QLocale();
3019 if ( !language.isEmpty() )
3020 {
3021 locale = QLocale( language );
3022 }
3023
3024 QDate date = locale.toDate( datestring, format );
3025 if ( !date.isValid() )
3026 {
3027 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
3028 date = QDate();
3029 }
3030 return QVariant( date );
3031}
3032
3033static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3034{
3035 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3036 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
3037 if ( format.isEmpty() && !language.isEmpty() )
3038 {
3039 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
3040 return QVariant( QTime() );
3041 }
3042
3043 if ( format.isEmpty() && language.isEmpty() )
3044 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
3045
3046 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3047 QLocale locale = QLocale();
3048 if ( !language.isEmpty() )
3049 {
3050 locale = QLocale( language );
3051 }
3052
3053 QTime time = locale.toTime( timestring, format );
3054 if ( !time.isValid() )
3055 {
3056 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
3057 time = QTime();
3058 }
3059 return QVariant( time );
3060}
3061
3062static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3063{
3064 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
3065}
3066
3067/*
3068 * DMS functions
3069 */
3070
3071static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3072{
3073 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3074 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3075 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3076
3077 QString formatString;
3078 if ( values.count() > 3 )
3079 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
3080
3082 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
3083 {
3085 }
3086 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
3087 {
3089 }
3090 else if ( !formatString.isEmpty() )
3091 {
3092 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
3093 return QVariant();
3094 }
3095
3096 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
3097 {
3098 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
3099 }
3100 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
3101 {
3102 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
3103 }
3104 else
3105 {
3106 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
3107 return QVariant();
3108 }
3109}
3110
3111static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3112{
3114 return floatToDegreeFormat( format, values, context, parent, node );
3115}
3116
3117static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3118{
3119 double value = 0.0;
3120 bool ok = false;
3121 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
3122
3123 return ok ? QVariant( value ) : QVariant();
3124}
3125
3126static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
3127{
3129 return floatToDegreeFormat( format, values, context, parent, node );
3130}
3131
3132static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3133{
3134 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3135 return static_cast< int >( decimalDegrees );
3136}
3137
3138static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3139{
3140 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3141 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3142 return static_cast< int >( remainder * 60 );
3143}
3144
3145static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3146{
3147 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
3148 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
3149 const double remainderInMinutes = remainder * 60;
3150 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
3151 // do not truncate to int, this function returns decimal seconds!
3152 return remainderSecondsFraction * 60;
3153}
3154
3155static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3156{
3157 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3158 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3159 qint64 seconds = d2.secsTo( d1 );
3160 return QVariant::fromValue( QgsInterval( seconds ) );
3161}
3162
3163static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3164{
3165 if ( !values.at( 0 ).canConvert<QDate>() )
3166 return QVariant();
3167
3168 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
3169 if ( !date.isValid() )
3170 return QVariant();
3171
3172 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
3173 // (to match PostgreSQL behavior)
3174 return date.dayOfWeek() % 7;
3175}
3176
3177static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3178{
3179 QVariant value = values.at( 0 );
3180 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3181 if ( inter.isValid() )
3182 {
3183 return QVariant( inter.days() );
3184 }
3185 else
3186 {
3187 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3188 return QVariant( d1.date().day() );
3189 }
3190}
3191
3192static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3193{
3194 QVariant value = values.at( 0 );
3195 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3196 if ( inter.isValid() )
3197 {
3198 return QVariant( inter.years() );
3199 }
3200 else
3201 {
3202 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3203 return QVariant( d1.date().year() );
3204 }
3205}
3206
3207static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3208{
3209 QVariant value = values.at( 0 );
3210 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3211 if ( inter.isValid() )
3212 {
3213 return QVariant( inter.months() );
3214 }
3215 else
3216 {
3217 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3218 return QVariant( d1.date().month() );
3219 }
3220}
3221
3222static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3223{
3224 QVariant value = values.at( 0 );
3225 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3226 if ( inter.isValid() )
3227 {
3228 return QVariant( inter.weeks() );
3229 }
3230 else
3231 {
3232 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3233 return QVariant( d1.date().weekNumber() );
3234 }
3235}
3236
3237static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3238{
3239 QVariant value = values.at( 0 );
3240 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3241 if ( inter.isValid() )
3242 {
3243 return QVariant( inter.hours() );
3244 }
3245 else
3246 {
3247 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3248 return QVariant( t1.hour() );
3249 }
3250}
3251
3252static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3253{
3254 QVariant value = values.at( 0 );
3255 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3256 if ( inter.isValid() )
3257 {
3258 return QVariant( inter.minutes() );
3259 }
3260 else
3261 {
3262 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3263 return QVariant( t1.minute() );
3264 }
3265}
3266
3267static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3268{
3269 QVariant value = values.at( 0 );
3270 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3271 if ( inter.isValid() )
3272 {
3273 return QVariant( inter.seconds() );
3274 }
3275 else
3276 {
3277 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3278 return QVariant( t1.second() );
3279 }
3280}
3281
3282static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3283{
3284 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3285 if ( dt.isValid() )
3286 {
3287 return QVariant( dt.toMSecsSinceEpoch() );
3288 }
3289 else
3290 {
3291 return QVariant();
3292 }
3293}
3294
3295static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3296{
3297 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3298 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3299 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3300}
3301
3302static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3303{
3304 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3305 if ( parent->hasEvalError() )
3306 {
3307 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3308 return QVariant();
3309 }
3310 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3311 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3312}
3313
3314static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3316 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3317 if ( parent->hasEvalError() )
3318 {
3319 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3320 return QVariant();
3321 }
3322 bool ok;
3323 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3324}
3325
3326double qDateTimeToDecimalYear( const QDateTime &dateTime )
3327{
3328 if ( !dateTime.isValid() )
3329 {
3330 return 0.0;
3331 }
3332
3333 const int year = dateTime.date().year();
3334 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3335 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3336 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3337 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3338 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3339}
3340
3341static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3342{
3343 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3344 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3345 if ( parent->hasEvalError() )
3346 {
3347 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3348 return QVariant();
3349 }
3350 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3351 if ( parent->hasEvalError() )
3352 {
3353 return QVariant();
3354 }
3355 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3356 if ( parent->hasEvalError() )
3357 {
3358 return QVariant();
3359 }
3360 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3361 if ( parent->hasEvalError() )
3362 {
3363 return QVariant();
3364 }
3365 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3366
3367 const QgsMagneticModel model( name, filePath );
3368 try
3369 {
3370 double declination = 0;
3371 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3372 {
3373 return declination;
3374 }
3375 else
3376 {
3377 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3378 }
3379 }
3380 catch ( QgsNotSupportedException &e )
3381 {
3382 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3383 }
3384 return QVariant();
3385}
3386
3387static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3388{
3389 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3390 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3391 if ( parent->hasEvalError() )
3392 {
3393 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3394 return QVariant();
3395 }
3396 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3397 if ( parent->hasEvalError() )
3398 {
3399 return QVariant();
3400 }
3401 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3402 if ( parent->hasEvalError() )
3403 {
3404 return QVariant();
3405 }
3406 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3407 if ( parent->hasEvalError() )
3408 {
3409 return QVariant();
3410 }
3411 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3412
3413 const QgsMagneticModel model( name, filePath );
3414 try
3415 {
3416 double inclination = 0;
3417 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3418 {
3419 return inclination;
3420 }
3421 else
3422 {
3423 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3424 }
3425 }
3426 catch ( QgsNotSupportedException &e )
3427 {
3428 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3429 }
3430 return QVariant();
3431}
3432
3433static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3434{
3435 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3436 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3437 if ( parent->hasEvalError() )
3438 {
3439 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3440 return QVariant();
3441 }
3442 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3443 if ( parent->hasEvalError() )
3444 {
3445 return QVariant();
3446 }
3447 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3448 if ( parent->hasEvalError() )
3449 {
3450 return QVariant();
3451 }
3452 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3453 if ( parent->hasEvalError() )
3454 {
3455 return QVariant();
3456 }
3457 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3458
3459 const QgsMagneticModel model( name, filePath );
3460 try
3461 {
3462 double declination = 0;
3463 double Bx = 0;
3464 double By = 0;
3465 double Bz = 0;
3466 double Bxt = 0;
3467 double Byt = 0;
3468 double Bzt = 0;
3469
3470 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3471 {
3472 double H = 0;
3473 double F = 0;
3474 double D = 0;
3475 double I = 0;
3476 double Ht = 0;
3477 double Ft = 0;
3478 double Dt = 0;
3479 double It = 0;
3480 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3481 {
3482 return Dt;
3483 }
3484 else
3485 {
3486 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3487 }
3488 return declination;
3489 }
3490 else
3491 {
3492 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3493 }
3494 }
3495 catch ( QgsNotSupportedException &e )
3496 {
3497 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3498 }
3499 return QVariant();
3500}
3501
3502static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3503{
3504 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3505 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3506 if ( parent->hasEvalError() )
3507 {
3508 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3509 return QVariant();
3510 }
3511 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3512 if ( parent->hasEvalError() )
3513 {
3514 return QVariant();
3515 }
3516 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3517 if ( parent->hasEvalError() )
3518 {
3519 return QVariant();
3520 }
3521 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3522 if ( parent->hasEvalError() )
3523 {
3524 return QVariant();
3525 }
3526 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3527
3528 const QgsMagneticModel model( name, filePath );
3529 try
3530 {
3531 double declination = 0;
3532 double Bx = 0;
3533 double By = 0;
3534 double Bz = 0;
3535 double Bxt = 0;
3536 double Byt = 0;
3537 double Bzt = 0;
3538
3539 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3540 {
3541 double H = 0;
3542 double F = 0;
3543 double D = 0;
3544 double I = 0;
3545 double Ht = 0;
3546 double Ft = 0;
3547 double Dt = 0;
3548 double It = 0;
3549 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3550 {
3551 return It;
3552 }
3553 else
3554 {
3555 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3556 }
3557 return declination;
3558 }
3559 else
3561 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3562 }
3563 }
3564 catch ( QgsNotSupportedException &e )
3565 {
3566 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3567 }
3568 return QVariant();
3569}
3570
3571#define ENSURE_GEOM_TYPE( f, g, geomtype ) \
3572 if ( !( f ).hasGeometry() ) \
3573 return QVariant(); \
3574 QgsGeometry g = ( f ).geometry(); \
3575 if ( ( g ).type() != ( geomtype ) ) \
3576 return QVariant();
3577
3578static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3579{
3580 FEAT_FROM_CONTEXT( context, f )
3582 if ( g.isMultipart() )
3583 {
3584 return g.asMultiPoint().at( 0 ).x();
3585 }
3586 else
3587 {
3588 return g.asPoint().x();
3589 }
3590}
3591
3592static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3593{
3594 FEAT_FROM_CONTEXT( context, f )
3596 if ( g.isMultipart() )
3597 {
3598 return g.asMultiPoint().at( 0 ).y();
3599 }
3600 else
3601 {
3602 return g.asPoint().y();
3603 }
3604}
3605
3606static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3607{
3608 FEAT_FROM_CONTEXT( context, f )
3610
3611 if ( g.isEmpty() )
3612 return QVariant();
3613
3614 const QgsAbstractGeometry *abGeom = g.constGet();
3615
3616 if ( g.isEmpty() || !abGeom->is3D() )
3617 return QVariant();
3618
3619 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3620 {
3621 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3622 if ( point )
3623 return point->z();
3624 }
3625 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3626 {
3627 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3628 {
3629 if ( collection->numGeometries() > 0 )
3630 {
3631 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3632 return point->z();
3633 }
3634 }
3635 }
3636
3637 return QVariant();
3638}
3639
3640static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3641{
3642 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3643 if ( geom.isNull() )
3644 return QVariant();
3645
3646 bool isValid = geom.isGeosValid();
3647
3648 return QVariant( isValid );
3649}
3650
3651static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3652{
3653 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3654 if ( geom.isNull() )
3655 return QVariant();
3656
3657 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3658#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
3660#else
3662#endif
3663 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3665 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3667
3668 const bool keepCollapsed = values.value( 2 ).toBool();
3669
3670 QgsGeometry valid;
3671 try
3672 {
3673 valid = geom.makeValid( method, keepCollapsed );
3674 }
3675 catch ( QgsNotSupportedException & )
3676 {
3677 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3678 return QVariant();
3679 }
3680
3681 return QVariant::fromValue( valid );
3682}
3683
3684static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3685{
3686 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3687 if ( geom.isNull() )
3688 return QVariant();
3689
3690 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3691 QVariantList array;
3692 for ( int i = 0; i < multiGeom.size(); ++i )
3693 {
3694 array += QVariant::fromValue( multiGeom.at( i ) );
3695 }
3696
3697 return array;
3698}
3699
3700static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3701{
3702 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3703 if ( geom.isNull() )
3704 return QVariant();
3705
3706 //if single point, return the point's x coordinate
3707 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3708 {
3709 return geom.asPoint().x();
3710 }
3711
3712 //otherwise return centroid x
3713 QgsGeometry centroid = geom.centroid();
3714 QVariant result( centroid.asPoint().x() );
3715 return result;
3716}
3717
3718static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3719{
3720 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3721 if ( geom.isNull() )
3722 return QVariant();
3723
3724 //if single point, return the point's y coordinate
3725 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3726 {
3727 return geom.asPoint().y();
3728 }
3729
3730 //otherwise return centroid y
3731 QgsGeometry centroid = geom.centroid();
3732 QVariant result( centroid.asPoint().y() );
3733 return result;
3734}
3735
3736static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3737{
3738 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3739 if ( geom.isNull() )
3740 return QVariant(); //or 0?
3741
3742 if ( !geom.constGet()->is3D() )
3743 return QVariant();
3744
3745 //if single point, return the point's z coordinate
3746 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3747 {
3749 if ( point )
3750 return point->z();
3751 }
3752 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3753 {
3755 {
3756 if ( collection->numGeometries() == 1 )
3757 {
3758 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3759 return point->z();
3760 }
3761 }
3762 }
3763
3764 return QVariant();
3765}
3766
3767static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3768{
3769 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3770 if ( geom.isNull() )
3771 return QVariant(); //or 0?
3772
3773 if ( !geom.constGet()->isMeasure() )
3774 return QVariant();
3775
3776 //if single point, return the point's m value
3777 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3778 {
3780 if ( point )
3781 return point->m();
3782 }
3783 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3784 {
3786 {
3787 if ( collection->numGeometries() == 1 )
3788 {
3789 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3790 return point->m();
3791 }
3792 }
3793 }
3794
3795 return QVariant();
3796}
3797
3798static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3799{
3800 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3801
3802 if ( geom.isNull() )
3803 return QVariant();
3804
3805 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3806
3807 if ( idx < 0 )
3808 {
3809 //negative idx
3810 int count = geom.constGet()->nCoordinates();
3811 idx = count + idx;
3812 }
3813 else
3814 {
3815 //positive idx is 1 based
3816 idx -= 1;
3817 }
3818
3819 QgsVertexId vId;
3820 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3821 {
3822 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3823 return QVariant();
3824 }
3825
3826 QgsPoint point = geom.constGet()->vertexAt( vId );
3827 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3828}
3829
3830static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3831{
3832 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3833
3834 if ( geom.isNull() )
3835 return QVariant();
3836
3837 QgsVertexId vId;
3838 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3839 {
3840 return QVariant();
3841 }
3842
3843 QgsPoint point = geom.constGet()->vertexAt( vId );
3844 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3845}
3846
3847static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3848{
3849 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3850
3851 if ( geom.isNull() )
3852 return QVariant();
3853
3854 QgsVertexId vId;
3855 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3856 {
3857 return QVariant();
3858 }
3859
3860 QgsPoint point = geom.constGet()->vertexAt( vId );
3861 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3862}
3863
3864static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3865{
3866 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3867
3868 if ( geom.isNull() )
3869 return QVariant();
3870
3871 bool ignoreClosing = false;
3872 if ( values.length() > 1 )
3873 {
3874 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3875 }
3876
3877 QgsMultiPoint *mp = new QgsMultiPoint();
3878
3879 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3880 for ( const QgsRingSequence &part : sequence )
3881 {
3882 for ( const QgsPointSequence &ring : part )
3883 {
3884 bool skipLast = false;
3885 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3886 {
3887 skipLast = true;
3888 }
3889
3890 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++i )
3891 {
3892 mp->addGeometry( ring.at( i ).clone() );
3893 }
3894 }
3895 }
3896
3897 return QVariant::fromValue( QgsGeometry( mp ) );
3898}
3899
3900static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3901{
3902 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3903
3904 if ( geom.isNull() )
3905 return QVariant();
3906
3907 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3908
3909 //OK, now we have a complete list of segmentized lines from the geometry
3911 for ( QgsLineString *line : linesToProcess )
3912 {
3913 for ( int i = 0; i < line->numPoints() - 1; ++i )
3914 {
3916 segment->setPoints( QgsPointSequence() << line->pointN( i ) << line->pointN( i + 1 ) );
3917 ml->addGeometry( segment );
3918 }
3919 delete line;
3920 }
3921
3922 return QVariant::fromValue( QgsGeometry( ml ) );
3923}
3924
3925static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3926{
3927 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3928
3929 if ( geom.isNull() )
3930 return QVariant();
3931
3933 if ( !curvePolygon && geom.isMultipart() )
3934 {
3936 {
3937 if ( collection->numGeometries() == 1 )
3938 {
3939 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3940 }
3941 }
3942 }
3943
3944 if ( !curvePolygon )
3945 return QVariant();
3946
3947 //idx is 1 based
3948 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3949
3950 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3951 return QVariant();
3952
3953 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3954 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3955 return result;
3956}
3957
3958static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3959{
3960 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3961
3962 if ( geom.isNull() )
3963 return QVariant();
3964
3966 if ( !collection )
3967 return QVariant();
3968
3969 //idx is 1 based
3970 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3971
3972 if ( idx < 0 || idx >= collection->numGeometries() )
3973 return QVariant();
3974
3975 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3976 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3977 return result;
3978}
3979
3980static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3981{
3982 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3983
3984 if ( geom.isNull() )
3985 return QVariant();
3986
3987 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3988 if ( !boundary )
3989 return QVariant();
3990
3991 return QVariant::fromValue( QgsGeometry( boundary ) );
3992}
3993
3994static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3995{
3996 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3997
3998 if ( geom.isNull() )
3999 return QVariant();
4000
4001 QgsGeometry merged = geom.mergeLines();
4002 if ( merged.isNull() )
4003 return QVariant();
4004
4005 return QVariant::fromValue( merged );
4006}
4007
4008static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4009{
4010 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4011 if ( geom.isNull() )
4012 return QVariant();
4013
4014 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4015 if ( geom2.isNull() )
4016 return QVariant();
4017
4018 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
4019 if ( sharedPaths.isNull() )
4020 return QVariant();
4021
4022 return QVariant::fromValue( sharedPaths );
4023}
4024
4025
4026static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4027{
4028 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4029
4030 if ( geom.isNull() )
4031 return QVariant();
4032
4033 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4034
4035 QgsGeometry simplified = geom.simplify( tolerance );
4036 if ( simplified.isNull() )
4037 return QVariant();
4038
4039 return simplified;
4040}
4041
4042static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4043{
4044 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4045
4046 if ( geom.isNull() )
4047 return QVariant();
4048
4049 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4050
4052
4053 QgsGeometry simplified = simplifier.simplify( geom );
4054 if ( simplified.isNull() )
4055 return QVariant();
4056
4057 return simplified;
4058}
4059
4060static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4061{
4062 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4063
4064 if ( geom.isNull() )
4065 return QVariant();
4066
4067 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
4068 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
4069 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4070 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
4071
4072 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
4073 if ( smoothed.isNull() )
4074 return QVariant();
4075
4076 return smoothed;
4077}
4078
4079static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4080{
4081 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4082
4083 if ( geom.isNull() )
4084 return QVariant();
4085
4086 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4087 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4088 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4089
4090 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
4091 if ( waved.isNull() )
4092 return QVariant();
4093
4094 return waved;
4095}
4096
4097static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4098{
4099 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4100
4101 if ( geom.isNull() )
4102 return QVariant();
4103
4104 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4105 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4106 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4107 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4108 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4109
4110 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4111 if ( waved.isNull() )
4112 return QVariant();
4113
4114 return waved;
4115}
4116
4117static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4118{
4119 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4120
4121 if ( geom.isNull() )
4122 return QVariant();
4123
4124 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4125 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4126 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4127
4128 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
4129 if ( waved.isNull() )
4130 return QVariant();
4131
4132 return waved;
4133}
4134
4135static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4136{
4137 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4138
4139 if ( geom.isNull() )
4140 return QVariant();
4141
4142 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4143 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4144 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4145 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4146 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4147
4148 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4149 if ( waved.isNull() )
4150 return QVariant();
4151
4152 return waved;
4153}
4154
4155static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4156{
4157 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4158
4159 if ( geom.isNull() )
4160 return QVariant();
4161
4162 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4163 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4164 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4165
4166 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
4167 if ( waved.isNull() )
4168 return QVariant();
4169
4170 return waved;
4171}
4172
4173static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4174{
4175 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4176
4177 if ( geom.isNull() )
4178 return QVariant();
4179
4180 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4181 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4182 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4183 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4184 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4185
4186 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4187 if ( waved.isNull() )
4188 return QVariant();
4189
4190 return waved;
4191}
4192
4193static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4194{
4195 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4196
4197 if ( geom.isNull() )
4198 return QVariant();
4199
4200 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4201 QVector< double > dashPattern;
4202 dashPattern.reserve( pattern.size() );
4203 for ( const QVariant &value : std::as_const( pattern ) )
4204 {
4205 bool ok = false;
4206 double v = value.toDouble( &ok );
4207 if ( ok )
4208 {
4209 dashPattern << v;
4210 }
4211 else
4212 {
4213 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4214 return QgsGeometry();
4215 }
4216 }
4217
4218 if ( dashPattern.size() % 2 != 0 )
4219 {
4220 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4221 return QgsGeometry();
4222 }
4223
4224 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4226 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4228 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4230 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4232 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4234 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4236 else
4237 {
4238 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4239 return QgsGeometry();
4240 }
4241
4242 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4244 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4246 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4248 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4250 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4252 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4254 else
4255 {
4256 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4257 return QgsGeometry();
4258 }
4259
4260 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4262 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4264 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4266 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4268 else
4269 {
4270 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4271 return QgsGeometry();
4272 }
4273
4274 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4275
4276 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4277 if ( result.isNull() )
4278 return QVariant();
4279
4280 return result;
4281}
4282
4283static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4284{
4285 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4286
4287 if ( geom.isNull() )
4288 return QVariant();
4289
4290 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4291 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4292 if ( densified.isNull() )
4293 return QVariant();
4294
4295 return densified;
4296}
4297
4298static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4299{
4300 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4301
4302 if ( geom.isNull() )
4303 return QVariant();
4304
4305 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4306 const QgsGeometry densified = geom.densifyByDistance( distance );
4307 if ( densified.isNull() )
4308 return QVariant();
4309
4310 return densified;
4311}
4312
4313static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4314{
4315 QVariantList list;
4316 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4317 {
4318 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4319 }
4320 else
4321 {
4322 list = values;
4323 }
4324
4325 QVector< QgsGeometry > parts;
4326 parts.reserve( list.size() );
4327 for ( const QVariant &value : std::as_const( list ) )
4328 {
4329 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4330 if ( part.isNull() )
4331 return QgsGeometry();
4332 parts << part;
4333 }
4334
4335 return QgsGeometry::collectGeometry( parts );
4336}
4337
4338static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4339{
4340 if ( values.count() < 2 || values.count() > 4 )
4341 {
4342 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4343 return QVariant();
4344 }
4345
4346 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4347 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4348 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4349 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4350 switch ( values.count() )
4351 {
4352 case 2:
4353 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4354 case 3:
4355 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4356 case 4:
4357 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4358 }
4359 return QVariant(); //avoid warning
4360}
4361
4362static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4363{
4364 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4365 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4366 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4367 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4368}
4369
4370static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4371{
4372 if ( values.empty() )
4373 {
4374 return QVariant();
4375 }
4376
4377 QVector<QgsPoint> points;
4378 points.reserve( values.count() );
4379
4380 auto addPoint = [&points]( const QgsGeometry &geom ) {
4381 if ( geom.isNull() )
4382 return;
4383
4384 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4385 return;
4386
4388 if ( !point )
4389 return;
4390
4391 points << *point;
4392 };
4393
4394 for ( const QVariant &value : values )
4395 {
4396 if ( value.userType() == QMetaType::Type::QVariantList )
4397 {
4398 const QVariantList list = value.toList();
4399 for ( const QVariant &v : list )
4400 {
4401 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4402 }
4403 }
4404 else
4405 {
4406 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4407 }
4408 }
4409
4410 if ( points.count() < 2 )
4411 return QVariant();
4412
4413 return QgsGeometry( new QgsLineString( points ) );
4414}
4415
4416static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4417{
4418 if ( values.count() < 1 )
4419 {
4420 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4421 return QVariant();
4422 }
4423
4424 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4425
4426 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4427 return outerRing; // if it's already a polygon we have nothing to do
4428
4429 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4430 return QVariant();
4431
4432 auto polygon = std::make_unique< QgsPolygon >();
4433
4434 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4435 if ( !exteriorRing && outerRing.isMultipart() )
4436 {
4438 {
4439 if ( collection->numGeometries() == 1 )
4440 {
4441 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4442 }
4443 }
4444 }
4445
4446 if ( !exteriorRing )
4447 return QVariant();
4448
4449 polygon->setExteriorRing( exteriorRing->segmentize() );
4450
4451
4452 for ( int i = 1; i < values.count(); ++i )
4453 {
4454 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4455 if ( ringGeom.isNull() )
4456 continue;
4457
4458 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4459 continue;
4460
4461 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4462 if ( !ring && ringGeom.isMultipart() )
4463 {
4465 {
4466 if ( collection->numGeometries() == 1 )
4467 {
4468 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4469 }
4470 }
4471 }
4472
4473 if ( !ring )
4474 continue;
4475
4476 polygon->addInteriorRing( ring->segmentize() );
4477 }
4478
4479 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4480}
4481
4482static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4483{
4484 auto tr = std::make_unique<QgsTriangle>();
4485 auto lineString = std::make_unique<QgsLineString>();
4486 lineString->clear();
4487
4488 for ( const QVariant &value : values )
4489 {
4490 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4491 if ( geom.isNull() )
4492 return QVariant();
4493
4494 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4495 return QVariant();
4496
4498 if ( !point && geom.isMultipart() )
4499 {
4501 {
4502 if ( collection->numGeometries() == 1 )
4503 {
4504 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4505 }
4506 }
4507 }
4508
4509 if ( !point )
4510 return QVariant();
4511
4512 lineString->addVertex( *point );
4513 }
4514
4515 tr->setExteriorRing( lineString.release() );
4516
4517 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4518}
4519
4520static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4521{
4522 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4523 if ( geom.isNull() )
4524 return QVariant();
4525
4526 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4527 return QVariant();
4528
4529 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4530 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4531
4532 if ( segment < 3 )
4533 {
4534 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4535 return QVariant();
4536 }
4538 if ( !point && geom.isMultipart() )
4539 {
4541 {
4542 if ( collection->numGeometries() == 1 )
4543 {
4544 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4545 }
4546 }
4547 }
4548 if ( !point )
4549 return QVariant();
4550
4551 QgsCircle circ( *point, radius );
4552 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4553}
4554
4555static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4556{
4557 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4558 if ( geom.isNull() )
4559 return QVariant();
4560
4561 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4562 return QVariant();
4563
4564 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4565 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4566 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4567 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4568 if ( segment < 3 )
4569 {
4570 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4571 return QVariant();
4572 }
4574 if ( !point && geom.isMultipart() )
4575 {
4577 {
4578 if ( collection->numGeometries() == 1 )
4579 {
4580 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4581 }
4582 }
4583 }
4584 if ( !point )
4585 return QVariant();
4586
4587 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4588 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4589}
4590
4591static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4592{
4593 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4594 if ( pt1.isNull() )
4595 return QVariant();
4596
4597 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4598 return QVariant();
4599
4600 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4601 if ( pt2.isNull() )
4602 return QVariant();
4603
4604 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4605 return QVariant();
4606
4607 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4608 if ( nbEdges < 3 )
4609 {
4610 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4611 return QVariant();
4612 }
4613
4614 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4616 {
4617 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4618 return QVariant();
4619 }
4620
4622 if ( !center && pt1.isMultipart() )
4623 {
4625 {
4626 if ( collection->numGeometries() == 1 )
4627 {
4628 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4629 }
4630 }
4631 }
4632 if ( !center )
4633 return QVariant();
4634
4636 if ( !corner && pt2.isMultipart() )
4637 {
4639 {
4640 if ( collection->numGeometries() == 1 )
4641 {
4642 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4643 }
4644 }
4645 }
4646 if ( !corner )
4647 return QVariant();
4648
4649 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4650
4651 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4652}
4653
4654static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4655{
4656 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4657 if ( pt1.isNull() )
4658 return QVariant();
4659 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4660 return QVariant();
4661
4662 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4663 if ( pt2.isNull() )
4664 return QVariant();
4665 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4666 return QVariant();
4667
4668 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4669 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4670 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4671
4672 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4673}
4674
4675static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4676{
4677 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4678 if ( pt1.isNull() )
4679 return QVariant();
4680 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4681 return QVariant();
4682
4683 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4684 if ( pt2.isNull() )
4685 return QVariant();
4686 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4687 return QVariant();
4688
4689 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4690 if ( pt3.isNull() )
4691 return QVariant();
4692 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4693 return QVariant();
4694
4695 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4696 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4697 {
4698 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4699 return QVariant();
4700 }
4701 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4702 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4703 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4704 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4705 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4706}
4707
4708static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4709{
4710 if ( geom.isNull() )
4711 return QVariant();
4712
4713 if ( idx < 0 )
4714 {
4715 idx += geom.constGet()->nCoordinates();
4716 }
4717 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4718 {
4719 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4720 return QVariant();
4721 }
4722 return QVariant::fromValue( geom.vertexAt( idx ) );
4723}
4724
4725// function used for the old $ style
4726static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4727{
4728 FEAT_FROM_CONTEXT( context, feature )
4729 const QgsGeometry geom = feature.geometry();
4730 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4731
4732 const QVariant v = pointAt( geom, idx, parent );
4733
4734 if ( !v.isNull() )
4735 return QVariant( v.value<QgsPoint>().x() );
4736 else
4737 return QVariant();
4738}
4739static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4740{
4741 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))
4742 {
4743 return fcnOldXat( values, f, parent, node );
4744 }
4745 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)
4746 {
4747 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4748 }
4749
4750 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4751 if ( geom.isNull() )
4752 {
4753 return QVariant();
4754 }
4755
4756 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4757
4758 const QVariant v = pointAt( geom, vertexNumber, parent );
4759 if ( !v.isNull() )
4760 return QVariant( v.value<QgsPoint>().x() );
4761 else
4762 return QVariant();
4763}
4764
4765// function used for the old $ style
4766static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4767{
4768 FEAT_FROM_CONTEXT( context, feature )
4769 const QgsGeometry geom = feature.geometry();
4770 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4771
4772 const QVariant v = pointAt( geom, idx, parent );
4773
4774 if ( !v.isNull() )
4775 return QVariant( v.value<QgsPoint>().y() );
4776 else
4777 return QVariant();
4778}
4779static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4780{
4781 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))
4782 {
4783 return fcnOldYat( values, f, parent, node );
4784 }
4785 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)
4786 {
4787 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4788 }
4789
4790 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4791 if ( geom.isNull() )
4792 {
4793 return QVariant();
4794 }
4795
4796 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4797
4798 const QVariant v = pointAt( geom, vertexNumber, parent );
4799 if ( !v.isNull() )
4800 return QVariant( v.value<QgsPoint>().y() );
4801 else
4802 return QVariant();
4803}
4804
4805static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4806{
4807 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4808 if ( geom.isNull() )
4809 {
4810 return QVariant();
4811 }
4812
4813 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4814
4815 const QVariant v = pointAt( geom, vertexNumber, parent );
4816 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4817 return QVariant( v.value<QgsPoint>().z() );
4818 else
4819 return QVariant();
4820}
4821
4822static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4823{
4824 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4825 if ( geom.isNull() )
4826 {
4827 return QVariant();
4828 }
4829
4830 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4831
4832 const QVariant v = pointAt( geom, vertexNumber, parent );
4833 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4834 return QVariant( v.value<QgsPoint>().m() );
4835 else
4836 return QVariant();
4837}
4838
4839
4840static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4841{
4842 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4843 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4844 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4845 return result;
4846}
4847
4848static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4849{
4850 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4851 if ( wkb.isNull() )
4852 return QVariant();
4853
4854 QgsGeometry geom;
4855 geom.fromWkb( wkb );
4856 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4857}
4858
4859static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4860{
4861 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4862 QgsOgcUtils::Context ogcContext;
4863 if ( context )
4864 {
4865 QgsWeakMapLayerPointer mapLayerPtr { context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4866 if ( mapLayerPtr )
4867 {
4868 ogcContext.layer = mapLayerPtr.data();
4869 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4870 }
4871 }
4872 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4873 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4874 return result;
4875}
4876
4877static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4878{
4879 FEAT_FROM_CONTEXT( context, f )
4881 QgsDistanceArea *calc = parent->geomCalculator();
4882 if ( calc )
4883 {
4884 try
4885 {
4886 double area = calc->measureArea( f.geometry() );
4887 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4888 return QVariant( area );
4889 }
4890 catch ( QgsCsException & )
4891 {
4892 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4893 return QVariant();
4894 }
4895 }
4896 else
4897 {
4898 return QVariant( f.geometry().area() );
4899 }
4900}
4901
4902static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4903{
4904 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4905
4906 if ( geom.type() != Qgis::GeometryType::Polygon )
4907 return QVariant();
4908
4909 return QVariant( geom.area() );
4910}
4911
4912static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4913{
4914 FEAT_FROM_CONTEXT( context, f )
4916 QgsDistanceArea *calc = parent->geomCalculator();
4917 if ( calc )
4918 {
4919 try
4920 {
4921 double len = calc->measureLength( f.geometry() );
4922 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4923 return QVariant( len );
4924 }
4925 catch ( QgsCsException & )
4926 {
4927 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4928 return QVariant();
4929 }
4930 }
4931 else
4932 {
4933 return QVariant( f.geometry().length() );
4934 }
4935}
4936
4937static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4938{
4939 FEAT_FROM_CONTEXT( context, f )
4941 QgsDistanceArea *calc = parent->geomCalculator();
4942 if ( calc )
4943 {
4944 try
4945 {
4946 double len = calc->measurePerimeter( f.geometry() );
4947 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4948 return QVariant( len );
4949 }
4950 catch ( QgsCsException & )
4951 {
4952 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4953 return QVariant();
4954 }
4955 }
4956 else
4957 {
4958 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4959 }
4960}
4961
4962static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4963{
4964 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4965
4966 if ( geom.type() != Qgis::GeometryType::Polygon )
4967 return QVariant();
4968
4969 //length for polygons = perimeter
4970 return QVariant( geom.length() );
4971}
4972
4973static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4974{
4975 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4976 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4977}
4978
4979static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4980{
4981 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4982 if ( geom.isNull() )
4983 return QVariant();
4984
4985 return QVariant( geom.constGet()->partCount() );
4986}
4987
4988static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4989{
4990 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4991 if ( geom.isNull() )
4992 return QVariant();
4993
4994 return QVariant( geom.isMultipart() );
4995}
4996
4997static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4998{
4999 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5000
5001 if ( geom.isNull() )
5002 return QVariant();
5003
5005 if ( curvePolygon )
5006 return QVariant( curvePolygon->numInteriorRings() );
5007
5009 if ( collection )
5010 {
5011 //find first CurvePolygon in collection
5012 for ( int i = 0; i < collection->numGeometries(); ++i )
5013 {
5014 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5015 if ( !curvePolygon )
5016 continue;
5017
5018 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
5019 }
5020 }
5021
5022 return QVariant();
5023}
5024
5025static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5026{
5027 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5028
5029 if ( geom.isNull() )
5030 return QVariant();
5031
5033 if ( curvePolygon )
5034 return QVariant( curvePolygon->ringCount() );
5035
5036 bool foundPoly = false;
5037 int ringCount = 0;
5039 if ( collection )
5040 {
5041 //find CurvePolygons in collection
5042 for ( int i = 0; i < collection->numGeometries(); ++i )
5043 {
5044 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
5045 if ( !curvePolygon )
5046 continue;
5047
5048 foundPoly = true;
5049 ringCount += curvePolygon->ringCount();
5050 }
5051 }
5052
5053 if ( !foundPoly )
5054 return QVariant();
5055
5056 return QVariant( ringCount );
5057}
5058
5059static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5060{
5061 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5062 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
5063 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
5064 return result;
5065}
5066
5067static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5068{
5069 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5070 return QVariant::fromValue( geom.boundingBox().width() );
5071}
5072
5073static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5074{
5075 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5076 return QVariant::fromValue( geom.boundingBox().height() );
5077}
5078
5079static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5080{
5081 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5082 if ( geom.isNull() )
5083 return QVariant();
5084
5086}
5087
5088static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5089{
5090 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5091 return QVariant::fromValue( geom.boundingBox().xMinimum() );
5092}
5093
5094static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5095{
5096 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5097 return QVariant::fromValue( geom.boundingBox().xMaximum() );
5098}
5099
5100static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5101{
5102 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5103 return QVariant::fromValue( geom.boundingBox().yMinimum() );
5104}
5105
5106static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5107{
5108 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5109 return QVariant::fromValue( geom.boundingBox().yMaximum() );
5110}
5111
5112static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5113{
5114 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5115
5116 if ( geom.isNull() || geom.isEmpty() )
5117 return QVariant();
5118
5119 if ( !geom.constGet()->is3D() )
5120 return QVariant();
5121
5122 double max = std::numeric_limits< double >::lowest();
5123
5124 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5125 {
5126 double z = ( *it ).z();
5127
5128 if ( max < z )
5129 max = z;
5130 }
5131
5132 if ( max == std::numeric_limits< double >::lowest() )
5133 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5134
5135 return QVariant( max );
5136}
5137
5138static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5139{
5140 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5141
5142 if ( geom.isNull() || geom.isEmpty() )
5143 return QVariant();
5144
5145 if ( !geom.constGet()->is3D() )
5146 return QVariant();
5147
5148 double min = std::numeric_limits< double >::max();
5149
5150 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5151 {
5152 double z = ( *it ).z();
5153
5154 if ( z < min )
5155 min = z;
5156 }
5157
5158 if ( min == std::numeric_limits< double >::max() )
5159 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5160
5161 return QVariant( min );
5162}
5163
5164static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5165{
5166 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5167
5168 if ( geom.isNull() || geom.isEmpty() )
5169 return QVariant();
5170
5171 if ( !geom.constGet()->isMeasure() )
5172 return QVariant();
5173
5174 double min = std::numeric_limits< double >::max();
5175
5176 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5177 {
5178 double m = ( *it ).m();
5179
5180 if ( m < min )
5181 min = m;
5182 }
5183
5184 if ( min == std::numeric_limits< double >::max() )
5185 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5186
5187 return QVariant( min );
5188}
5189
5190static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5191{
5192 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5193
5194 if ( geom.isNull() || geom.isEmpty() )
5195 return QVariant();
5196
5197 if ( !geom.constGet()->isMeasure() )
5198 return QVariant();
5199
5200 double max = std::numeric_limits< double >::lowest();
5201
5202 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5203 {
5204 double m = ( *it ).m();
5205
5206 if ( max < m )
5207 max = m;
5208 }
5209
5210 if ( max == std::numeric_limits< double >::lowest() )
5211 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5212
5213 return QVariant( max );
5214}
5215
5216static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5217{
5218 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5220 if ( !curve )
5221 {
5222 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5223 return QVariant();
5224 }
5225
5226 return QVariant( curve->sinuosity() );
5227}
5228
5229static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5230{
5231 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5232 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5233 if ( !curve )
5234 {
5235 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5236 return QVariant();
5237 }
5238
5239 return QVariant( curve->straightDistance2d() );
5240}
5241
5242static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5243{
5244 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5246
5247 if ( !poly )
5248 {
5249 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5250 return QVariant();
5251 }
5252
5253 return QVariant( poly->roundness() );
5254}
5255
5256
5257static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5258{
5259 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5260 if ( geom.isNull() )
5261 return QVariant();
5262
5263 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5264 flipped->swapXy();
5265 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5266}
5267
5268static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5269{
5270 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5271 if ( fGeom.isNull() )
5272 return QVariant();
5273
5274 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5275 if ( !curve && fGeom.isMultipart() )
5276 {
5278 {
5279 if ( collection->numGeometries() == 1 )
5280 {
5281 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5282 }
5283 }
5284 }
5285
5286 if ( !curve )
5287 return QVariant();
5288
5289 return QVariant::fromValue( curve->isClosed() );
5290}
5291
5292static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5293{
5294 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5295
5296 if ( geom.isNull() )
5297 return QVariant();
5298
5299 QVariant result;
5300 if ( !geom.isMultipart() )
5301 {
5303
5304 if ( !line )
5305 return QVariant();
5306
5307 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5308 closedLine->close();
5309
5310 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5311 }
5312 else
5313 {
5315 if ( !collection )
5316 return QVariant();
5317
5318 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5319
5320 for ( int i = 0; i < collection->numGeometries(); ++i )
5321 {
5322 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5323 {
5324 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5325 closedLine->close();
5326
5327 closed->addGeometry( closedLine.release() );
5328 }
5329 }
5330 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5331 }
5332
5333 return result;
5334}
5335
5336static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5337{
5338 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5339 if ( fGeom.isNull() )
5340 return QVariant();
5341
5342 return QVariant::fromValue( fGeom.isEmpty() );
5343}
5344
5345static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5346{
5347 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5348 return QVariant::fromValue( true );
5349
5350 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5351 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5352}
5353
5354static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5355{
5356 if ( values.length() < 2 || values.length() > 3 )
5357 return QVariant();
5358
5359 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5360 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5361
5362 if ( fGeom.isNull() || sGeom.isNull() )
5363 return QVariant();
5364
5365 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5366
5367 if ( values.length() == 2 )
5368 {
5369 //two geometry arguments, return relation
5370 QString result = engine->relate( sGeom.constGet() );
5371 return QVariant::fromValue( result );
5372 }
5373 else
5374 {
5375 //three arguments, test pattern
5376 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5377 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5378 return QVariant::fromValue( result );
5379 }
5380}
5381
5382static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5383{
5384 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5385 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5386 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5387}
5388static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5389{
5390 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5391 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5392 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5393}
5394static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5395{
5396 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5397 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5398 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5399}
5400static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5401{
5402 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5403 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5404 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5405}
5406static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5407{
5408 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5409 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5410 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5411}
5412static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5413{
5414 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5415 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5416 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5417}
5418static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5419{
5420 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5421 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5422 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5423}
5424static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5425{
5426 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5427 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5428 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5429}
5430
5431static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5432{
5433 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5434 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5435 return fGeom.isExactlyEqual( sGeom ) ? TVL_True : TVL_False;
5436}
5437
5438static QVariant fcnIsEqualsExact( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5439{
5440 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5441 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5442 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5443
5444 bool ok;
5445 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5446 if ( !ok )
5447 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5448
5449 QVariant ret = TVL_False;
5450 try
5451 {
5452 ret = fGeom.isExactlyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5453 }
5454 catch ( QgsNotSupportedException &e )
5455 {
5456 SET_EVAL_ERROR( e.what() );
5457 }
5458 return ret;
5459}
5460
5461static QVariant fcnIsEqualsTopological( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5462{
5463 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5464 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5465 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5466
5467 bool ok;
5468 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
5469 if ( !ok )
5470 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5471
5472 QVariant ret = TVL_False;
5473 try
5474 {
5475 ret = fGeom.isTopologicallyEqual( sGeom, backend ) ? TVL_True : TVL_False;
5476 }
5477 catch ( QgsNotSupportedException &e )
5478 {
5479 SET_EVAL_ERROR( e.what() );
5480 }
5481 return ret;
5482}
5483
5484static QVariant fcnIsEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5485{
5486 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5487 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5488 const QString backendStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5489
5490 bool ok;
5491 Qgis::GeometryBackend backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::QGIS, false, &ok );
5492 if ( !ok )
5493 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
5494
5495 double epsilon = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5496
5497 QVariant ret = TVL_False;
5498 try
5499 {
5500 ret = fGeom.isFuzzyEqual( sGeom, epsilon, backend ) ? TVL_True : TVL_False;
5501 }
5502 catch ( QgsNotSupportedException &e )
5503 {
5504 SET_EVAL_ERROR( e.what() );
5505 }
5506 return ret;
5507}
5508
5509static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5510{
5511 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5512 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5513 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5514 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5515 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5516 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5517
5519 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5520 capStyle = Qgis::EndCapStyle::Flat;
5521 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5522 capStyle = Qgis::EndCapStyle::Square;
5523
5525 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5526 joinStyle = Qgis::JoinStyle::Miter;
5527 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5528 joinStyle = Qgis::JoinStyle::Bevel;
5529
5530 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5531 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5532 return result;
5533}
5534
5535static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5536{
5537 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5538 const QgsGeometry reoriented = fGeom.forceRHR();
5539 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5540}
5541
5542static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5543{
5544 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5545 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5546 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5547}
5548
5549static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5550{
5551 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5552 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5553 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5554}
5555
5556static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5557{
5558 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5560 if ( !pt && fGeom.isMultipart() )
5561 {
5563 {
5564 if ( collection->numGeometries() == 1 )
5565 {
5566 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5567 }
5568 }
5569 }
5570
5571 if ( !pt )
5572 {
5573 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5574 return QVariant();
5575 }
5576
5577 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5578 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5579 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5580 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5581
5582 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5583 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5584 return result;
5585}
5586
5587static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5588{
5589 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5590 if ( fGeom.type() != Qgis::GeometryType::Line )
5591 {
5592 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5593 return QVariant();
5594 }
5595
5596 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5597 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5598 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5599
5600 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5601 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5602 return result;
5603}
5604
5605static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5606{
5607 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5608 if ( fGeom.type() != Qgis::GeometryType::Line )
5609 {
5610 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5611 return QVariant();
5612 }
5613
5614 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5615
5616 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5617 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5618 return result;
5619}
5620
5621static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5622{
5623 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5624 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5625 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5626 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5627 if ( joinInt < 1 || joinInt > 3 )
5628 return QVariant();
5629 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5630
5631 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5632
5633 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5634 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5635 return result;
5636}
5637
5638static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5639{
5640 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5641 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5642 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5643
5644 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5645 if ( joinInt < 1 || joinInt > 3 )
5646 return QVariant();
5647 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5648
5649 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5650
5651 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5652 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5653 return result;
5654}
5655
5656static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5657{
5658 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5659 const double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5660 const double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5661
5662 const double deflectionStart = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5663 const double deflectionEnd = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5664
5665 QgsGeometry geom = fGeom.extendLine( distStart, distEnd, deflectionStart, deflectionEnd );
5666 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5667 return result;
5668}
5669
5670static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5671{
5672 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5673 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5674 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5675 fGeom.translate( dx, dy );
5676 return QVariant::fromValue( fGeom );
5677}
5678
5679static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5680{
5681 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5682 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5683 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent ) : QgsGeometry();
5684 const bool perPart = values.value( 3 ).toBool();
5685
5686 if ( center.isNull() && perPart && fGeom.isMultipart() )
5687 {
5688 // no explicit center, rotating per part
5689 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5690 // the result is equivalent to setting perPart as false anyway)
5691 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5692 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5693 {
5694 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5695 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5696 t.rotate( -rotation );
5697 t.translate( -partCenter.x(), -partCenter.y() );
5698 ( *it )->transform( t );
5699 }
5700 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5701 }
5702 else
5703 {
5704 QgsPointXY pt;
5705 if ( center.isEmpty() )
5706 {
5707 // if center wasn't specified, use bounding box centroid
5708 pt = fGeom.boundingBox().center();
5709 }
5711 {
5712 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5713 return QVariant();
5714 }
5715 else
5716 {
5718 }
5719
5720 fGeom.rotate( rotation, pt );
5721 return QVariant::fromValue( fGeom );
5722 }
5723}
5724
5725static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5726{
5727 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5728 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5729 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5730 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent ) : QgsGeometry();
5731
5732 QgsPointXY pt;
5733 if ( center.isNull() )
5734 {
5735 // if center wasn't specified, use bounding box centroid
5736 pt = fGeom.boundingBox().center();
5737 }
5739 {
5740 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5741 return QVariant();
5742 }
5743 else
5744 {
5745 pt = center.asPoint();
5746 }
5747
5748 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5749 t.scale( xScale, yScale );
5750 t.translate( -pt.x(), -pt.y() );
5751 fGeom.transform( t );
5752 return QVariant::fromValue( fGeom );
5753}
5754
5755static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5756{
5757 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5758 if ( fGeom.isNull() )
5759 {
5760 return QVariant();
5761 }
5762
5763 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5764 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5765
5766 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5767
5768 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5769 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5770
5771 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5772 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5773 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5774 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5775
5776 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5777 {
5778 fGeom.get()->addZValue( 0 );
5779 }
5780 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5781 {
5782 fGeom.get()->addMValue( 0 );
5783 }
5784
5785 QTransform transform;
5786 transform.translate( deltaX, deltaY );
5787 transform.rotate( rotationZ );
5788 transform.scale( scaleX, scaleY );
5789 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5790
5791 return QVariant::fromValue( fGeom );
5792}
5793
5794
5795static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5796{
5797 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5798 QgsGeometry geom = fGeom.centroid();
5799 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5800 return result;
5801}
5802static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5803{
5804 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5805 QgsGeometry geom = fGeom.pointOnSurface();
5806 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5807 return result;
5808}
5809
5810static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5811{
5812 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5813 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5814 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5815 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5816 return result;
5817}
5818
5819static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5820{
5821 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5822 QgsGeometry geom = fGeom.convexHull();
5823 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5824 return result;
5825}
5826
5827#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
5828static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5829{
5830 try
5831 {
5832 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5833 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5834 const bool allowHoles = values.value( 2 ).toBool();
5835 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5836 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5837 return result;
5838 }
5839 catch ( QgsCsException &cse )
5840 {
5841 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5842 return QVariant();
5843 }
5844}
5845#endif
5846
5847static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5848{
5849 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5850 int segments = 36;
5851 if ( values.length() == 2 )
5852 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5853 if ( segments < 0 )
5854 {
5855 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5856 return QVariant();
5857 }
5858
5859 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5860 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5861 return result;
5862}
5863
5864static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5865{
5866 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5868 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5869 return result;
5870}
5871
5872static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5873{
5874 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5875
5876 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5877 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5878 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5879
5880 double area, angle, width, height;
5881 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5882
5883 if ( geom.isNull() )
5884 {
5885 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5886 return QVariant();
5887 }
5888 return angle;
5889}
5890
5891static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5892{
5893 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5894 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5895 QgsGeometry geom = fGeom.difference( sGeom );
5896 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5897 return result;
5898}
5899
5900static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5901{
5902 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5903 return QVariant();
5904
5905 // two variants, one for geometry, one for string
5906
5907 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5908 if ( !fGeom.isNull() )
5909 {
5910 QVariant result;
5911 if ( !fGeom.isMultipart() )
5912 {
5913 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5914 if ( !curve )
5915 return QVariant();
5916
5917 QgsCurve *reversed = curve->reversed();
5918 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5919 }
5920 else
5921 {
5923 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5924 for ( int i = 0; i < collection->numGeometries(); ++i )
5925 {
5926 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5927 {
5928 reversed->addGeometry( curve->reversed() );
5929 }
5930 else
5931 {
5932 reversed->addGeometry( collection->geometryN( i )->clone() );
5933 }
5934 }
5935 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5936 }
5937 return result;
5938 }
5939
5940 //fall back to string variant
5941 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5942 std::reverse( string.begin(), string.end() );
5943 return string;
5944}
5945
5946static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5947{
5948 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5949 if ( fGeom.isNull() )
5950 return QVariant();
5951
5953 if ( !curvePolygon && fGeom.isMultipart() )
5954 {
5956 {
5957 if ( collection->numGeometries() == 1 )
5958 {
5959 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5960 }
5961 }
5962 }
5963
5964 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5965 return QVariant();
5966
5967 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5968 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5969 return result;
5970}
5971
5972static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5973{
5974 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5975 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5976 return QVariant( fGeom.distance( sGeom ) );
5977}
5978
5979static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5980{
5981 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5982 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5983
5984 double res = -1;
5985 if ( values.length() == 3 && values.at( 2 ).isValid() )
5986 {
5987 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5988 densify = std::clamp( densify, 0.0, 1.0 );
5989 res = g1.hausdorffDistanceDensify( g2, densify );
5990 }
5991 else
5992 {
5993 res = g1.hausdorffDistance( g2 );
5994 }
5995
5996 return res > -1 ? QVariant( res ) : QVariant();
5997}
5998
5999static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6000{
6001 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6002 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6003 QgsGeometry geom = fGeom.intersection( sGeom );
6004 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6005 return result;
6006}
6007static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6008{
6009 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6010 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6011 QgsGeometry geom = fGeom.symDifference( sGeom );
6012 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6013 return result;
6014}
6015static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6016{
6017 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6018 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6019 QgsGeometry geom = fGeom.combine( sGeom );
6020 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6021 return result;
6022}
6023
6024static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6025{
6026 if ( values.length() < 1 || values.length() > 2 )
6027 return QVariant();
6028
6029 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6030 int prec = 8;
6031 if ( values.length() == 2 )
6032 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6033 QString wkt = fGeom.asWkt( prec );
6034 return QVariant( wkt );
6035}
6036
6037static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6038{
6039 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6040 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
6041}
6042
6043static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6044{
6045 if ( values.length() != 2 )
6046 {
6047 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
6048 return QVariant();
6049 }
6050
6051 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6052 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6053
6054 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6055 if ( !pt1 && fGeom1.isMultipart() )
6056 {
6058 {
6059 if ( collection->numGeometries() == 1 )
6060 {
6061 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6062 }
6063 }
6064 }
6065
6066 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6067 if ( !pt2 && fGeom2.isMultipart() )
6068 {
6070 {
6071 if ( collection->numGeometries() == 1 )
6072 {
6073 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6074 }
6075 }
6076 }
6077
6078 if ( !pt1 || !pt2 )
6079 {
6080 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
6081 return QVariant();
6082 }
6083
6084 // Code from PostGIS
6085 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
6086 {
6087 if ( pt1->y() < pt2->y() )
6088 return 0.0;
6089 else if ( pt1->y() > pt2->y() )
6090 return M_PI;
6091 else
6092 return 0;
6093 }
6094
6095 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
6096 {
6097 if ( pt1->x() < pt2->x() )
6098 return M_PI_2;
6099 else if ( pt1->x() > pt2->x() )
6100 return M_PI + ( M_PI_2 );
6101 else
6102 return 0;
6103 }
6104
6105 if ( pt1->x() < pt2->x() )
6106 {
6107 if ( pt1->y() < pt2->y() )
6108 {
6109 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
6110 }
6111 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
6112 {
6113 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI_2 );
6114 }
6115 }
6116
6117 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
6118 {
6119 if ( pt1->y() > pt2->y() )
6120 {
6121 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI;
6122 }
6123 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
6124 {
6125 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI_2 ) );
6126 }
6127 }
6128}
6129
6130static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6131{
6132 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6133 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6134 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6135 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
6136
6137 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
6138 {
6139 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
6140 return QVariant();
6141 }
6142
6143 const QgsPointXY point1 = geom1.asPoint();
6144 const QgsPointXY point2 = geom2.asPoint();
6145 if ( point1.isEmpty() || point2.isEmpty() )
6146 {
6147 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
6148 return QVariant();
6149 }
6150
6152 if ( context )
6153 {
6154 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6155
6156 if ( !sourceCrs.isValid() )
6157 {
6158 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
6159 }
6160
6161 if ( ellipsoid.isEmpty() )
6162 {
6163 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
6164 }
6165 }
6166
6167 if ( !sourceCrs.isValid() )
6168 {
6169 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
6170 return QVariant();
6171 }
6172
6173 QgsDistanceArea da;
6174 da.setSourceCrs( sourceCrs, tContext );
6175 if ( !da.setEllipsoid( ellipsoid ) )
6176 {
6177 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
6178 return QVariant();
6179 }
6180
6181 try
6182 {
6183 const double bearing = da.bearing( point1, point2 );
6184 if ( std::isfinite( bearing ) )
6185 {
6186 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
6187 }
6188 }
6189 catch ( QgsCsException &cse )
6190 {
6191 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
6192 return QVariant();
6193 }
6194 return QVariant();
6195}
6196
6197static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6198{
6199 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6200
6202 {
6203 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
6204 return QVariant();
6205 }
6206
6207 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6208 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6209 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
6210
6211 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef() );
6212 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
6213
6214 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
6215}
6216
6217static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6218{
6219 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6220 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6221
6222 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
6223 if ( !pt1 && fGeom1.isMultipart() )
6224 {
6226 {
6227 if ( collection->numGeometries() == 1 )
6228 {
6229 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6230 }
6231 }
6232 }
6233 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6234 if ( !pt2 && fGeom2.isMultipart() )
6235 {
6237 {
6238 if ( collection->numGeometries() == 1 )
6239 {
6240 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6241 }
6242 }
6243 }
6244
6245 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) || !pt1 || !pt2 )
6246 {
6247 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6248 return QVariant();
6249 }
6250
6251 return pt1->inclination( *pt2 );
6252}
6253
6254static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6255{
6256 if ( values.length() != 3 )
6257 return QVariant();
6258
6259 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6260 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6261 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6262
6263 QgsGeometry geom = fGeom.extrude( x, y );
6264
6265 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6266 return result;
6267}
6268
6269static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6270{
6271 if ( values.length() < 2 )
6272 return QVariant();
6273
6274 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6275
6276 if ( !fGeom.isMultipart() )
6277 return values.at( 0 );
6278
6279 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6280 QVariant cachedExpression;
6281 if ( ctx )
6282 cachedExpression = ctx->cachedValue( expString );
6283 QgsExpression expression;
6284
6285 if ( cachedExpression.isValid() )
6286 {
6287 expression = cachedExpression.value<QgsExpression>();
6288 }
6289 else
6290 expression = QgsExpression( expString );
6291
6292 bool asc = values.value( 2 ).toBool();
6293
6294 QgsExpressionContext *unconstedContext = nullptr;
6295 QgsFeature f;
6296 if ( ctx )
6297 {
6298 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6299 // so no reason to worry
6300 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6301 f = ctx->feature();
6302 }
6303 else
6304 {
6305 // If there's no context provided, create a fake one
6306 unconstedContext = new QgsExpressionContext();
6307 }
6308
6310 Q_ASSERT( collection ); // Should have failed the multipart check above
6311
6313 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6314 QgsExpressionSorter sorter( orderBy );
6315
6316 QList<QgsFeature> partFeatures;
6317 partFeatures.reserve( collection->partCount() );
6318 for ( int i = 0; i < collection->partCount(); ++i )
6319 {
6320 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6321 partFeatures << f;
6322 }
6323
6324 sorter.sortFeatures( partFeatures, unconstedContext );
6325
6327
6328 Q_ASSERT( orderedGeom );
6329
6330 while ( orderedGeom->partCount() )
6331 orderedGeom->removeGeometry( 0 );
6332
6333 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6334 {
6335 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6336 }
6337
6338 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6339
6340 if ( !ctx )
6341 delete unconstedContext;
6342
6343 return result;
6344}
6345
6346static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6347{
6348 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6349 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6350
6351 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6352
6353 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6354 return result;
6355}
6356
6357static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6358{
6359 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6360 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6361
6362 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6363
6364 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6365 return result;
6366}
6367
6368static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6369{
6370 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6371 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6372
6373 QgsGeometry geom = lineGeom.interpolate( distance );
6374
6375 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6376 return result;
6377}
6378
6379static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6380{
6381 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6382 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6383 const bool use3DDistance = values.at( 2 ).toBool();
6384
6385 double x, y, z, distance;
6386
6388 if ( !line )
6389 {
6390 return QVariant();
6391 }
6392
6393 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6394 {
6395 QgsPoint point( x, y );
6396 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6397 {
6398 point.addZValue( z );
6399 }
6400 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6401 }
6402
6403 return QVariant();
6404}
6405
6406static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6407{
6408 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6409 if ( lineGeom.type() != Qgis::GeometryType::Line )
6410 {
6411 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6412 return QVariant();
6413 }
6414
6415 const QgsCurve *curve = nullptr;
6416 if ( !lineGeom.isMultipart() )
6417 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6418 else
6419 {
6421 {
6422 if ( collection->numGeometries() > 0 )
6423 {
6424 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6425 }
6426 }
6427 }
6428 if ( !curve )
6429 return QVariant();
6430
6431 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6432 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6433
6434 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6435 QgsGeometry result( std::move( substring ) );
6436 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6437}
6438
6439static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6440{
6441 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6442 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6443
6444 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6445}
6446
6447static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6448{
6449 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6450 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6451 if ( vertex < 0 )
6452 {
6453 //negative idx
6454 int count = geom.constGet()->nCoordinates();
6455 vertex = count + vertex;
6456 }
6457
6458 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6459}
6460
6461static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6462{
6463 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6464 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6465 if ( vertex < 0 )
6466 {
6467 //negative idx
6468 int count = geom.constGet()->nCoordinates();
6469 vertex = count + vertex;
6470 }
6471
6472 return geom.distanceToVertex( vertex );
6473}
6474
6475static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6476{
6477 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6478 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6479
6480 double distance = lineGeom.lineLocatePoint( pointGeom );
6481
6482 return distance >= 0 ? distance : QVariant();
6483}
6484
6485static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6486{
6487 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6488 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6489 const bool use3DDistance = values.at( 2 ).toBool();
6490
6491 double x, y, z, distance;
6492
6494 if ( !line )
6495 {
6496 return QVariant();
6497 }
6498
6499 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6500 return found ? distance : QVariant();
6501}
6502
6503static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6504{
6505 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6506 {
6507 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6508 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6509 }
6510
6511 if ( values.length() >= 1 )
6512 {
6513 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6514 return QVariant( qlonglong( std::round( number ) ) );
6515 }
6516
6517 return QVariant();
6518}
6519
6520static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6521{
6522 Q_UNUSED( values )
6523 Q_UNUSED( parent )
6524 return M_PI;
6525}
6526
6527static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6528{
6529 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6530 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6531 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6532 if ( places < 0 )
6533 {
6534 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6535 return QVariant();
6536 }
6537
6538 const bool omitGroupSeparator = values.value( 3 ).toBool();
6539 const bool trimTrailingZeros = values.value( 4 ).toBool();
6540
6541 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6542 if ( !omitGroupSeparator )
6543 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6544 else
6545 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6546
6547 QString res = locale.toString( value, 'f', places );
6548
6549 if ( trimTrailingZeros )
6550 {
6551 const QChar decimal = locale.decimalPoint().at( 0 );
6552 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6553
6554 if ( res.contains( decimal ) )
6555 {
6556 int trimPoint = res.length() - 1;
6557
6558 while ( res.at( trimPoint ) == zeroDigit )
6559 trimPoint--;
6560
6561 if ( res.at( trimPoint ) == decimal )
6562 trimPoint--;
6563
6564 res.truncate( trimPoint + 1 );
6565 }
6566 }
6567
6568 return res;
6569}
6570
6571static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6572{
6573 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6574 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6575 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6576
6577 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6578 if ( format.indexOf( "Z" ) > 0 )
6579 datetime = datetime.toUTC();
6580
6581 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6582 return locale.toString( datetime, format );
6583}
6584
6585static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6586{
6587 const QVariant variant = values.at( 0 );
6588 bool isQColor;
6589 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6590 if ( !color.isValid() )
6591 return QVariant();
6592
6593 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6594 if ( color.spec() == QColor::Spec::Cmyk )
6595 {
6596 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() )
6597 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6598 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6599 }
6600 else
6601 {
6602 const float avg = ( color.redF() + color.greenF() + color.blueF() )
6603 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6604 color.setRgbF( avg, avg, avg, alpha );
6605 }
6606
6607 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6608}
6609
6610static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6611{
6612 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6613 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6614 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6615 if ( ratio > 1 )
6616 {
6617 ratio = 1;
6618 }
6619 else if ( ratio < 0 )
6620 {
6621 ratio = 0;
6622 }
6623
6624 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6625 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6626 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6627 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6628
6629 QColor newColor( red, green, blue, alpha );
6630
6631 return QgsSymbolLayerUtils::encodeColor( newColor );
6632}
6633
6634static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6635{
6636 const QVariant variant1 = values.at( 0 );
6637 const QVariant variant2 = values.at( 1 );
6638
6639 if ( variant1.userType() != variant2.userType() )
6640 {
6641 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6642 return QVariant();
6643 }
6644
6645 bool isQColor;
6646 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6647 if ( !color1.isValid() )
6648 return QVariant();
6649
6650 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6651 if ( !color2.isValid() )
6652 return QVariant();
6653
6654 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6655 {
6656 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6657 return QVariant();
6658 }
6659
6660 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6661
6662 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6663 // NOLINTBEGIN(bugprone-narrowing-conversions)
6664
6665 QColor newColor;
6666 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6667 if ( color1.spec() == QColor::Spec::Cmyk )
6668 {
6669 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6670 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6671 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6672 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6673 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6674 }
6675 else
6676 {
6677 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6678 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6679 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6680 newColor = QColor::fromRgbF( red, green, blue, alpha );
6681 }
6682
6683 // NOLINTEND(bugprone-narrowing-conversions)
6684
6685 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6686}
6687
6688static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6689{
6690 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6691 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6692 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6693 QColor color = QColor( red, green, blue );
6694 if ( !color.isValid() )
6695 {
6696 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6697 color = QColor( 0, 0, 0 );
6698 }
6699
6700 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6701}
6702
6703static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6704{
6705 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6706 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6707 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6708 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6709 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6710 if ( !color.isValid() )
6711 {
6712 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6713 return QVariant();
6714 }
6715
6716 return color;
6717}
6718
6719static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6720{
6721 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6722 QVariant value = node->eval( parent, context );
6723 if ( parent->hasEvalError() )
6724 {
6725 parent->setEvalErrorString( QString() );
6726 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6728 value = node->eval( parent, context );
6730 }
6731 return value;
6732}
6733
6734static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6735{
6736 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6738 QVariant value = node->eval( parent, context );
6740 if ( value.toBool() )
6741 {
6742 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6744 value = node->eval( parent, context );
6746 }
6747 else
6748 {
6749 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6751 value = node->eval( parent, context );
6753 }
6754 return value;
6755}
6756
6757static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6758{
6759 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6760 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6761 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6762 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6763 QColor color = QColor( red, green, blue, alpha );
6764 if ( !color.isValid() )
6765 {
6766 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6767 color = QColor( 0, 0, 0 );
6768 }
6769 return QgsSymbolLayerUtils::encodeColor( color );
6770}
6771
6772QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6773{
6774 QgsGradientColorRamp expRamp;
6775 const QgsColorRamp *ramp = nullptr;
6776 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6777 {
6778 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6779 ramp = &expRamp;
6780 }
6781 else
6782 {
6783 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6784 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6785 if ( !ramp )
6787 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6788 return QVariant();
6789 }
6790 }
6791
6792 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6793 QColor color = ramp->color( value );
6794 return color;
6795}
6796
6797QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6798{
6799 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6800 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6801}
6802
6803static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6804{
6805 // Hue ranges from 0 - 360
6806 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6807 // Saturation ranges from 0 - 100
6808 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6809 // Lightness ranges from 0 - 100
6810 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6811
6812 QColor color = QColor::fromHslF( hue, saturation, lightness );
6813
6814 if ( !color.isValid() )
6815 {
6816 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6817 color = QColor( 0, 0, 0 );
6818 }
6819
6820 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6821}
6822
6823static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6824{
6825 // Hue ranges from 0 - 360
6826 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6827 // Saturation ranges from 0 - 100
6828 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6829 // Lightness ranges from 0 - 100
6830 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6831 // Alpha ranges from 0 - 255
6832 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6833
6834 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6835 if ( !color.isValid() )
6836 {
6837 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6838 color = QColor( 0, 0, 0 );
6839 }
6840 return QgsSymbolLayerUtils::encodeColor( color );
6841}
6842
6843static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6844{
6845 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6846 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6847 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6848 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6849
6850 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6851 if ( !color.isValid() )
6852 {
6853 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6854 return QVariant();
6855 }
6856
6857 return color;
6858}
6859
6860static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6861{
6862 // Hue ranges from 0 - 360
6863 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6864 // Saturation ranges from 0 - 100
6865 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6866 // Value ranges from 0 - 100
6867 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6868
6869 QColor color = QColor::fromHsvF( hue, saturation, value );
6870
6871 if ( !color.isValid() )
6872 {
6873 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6874 color = QColor( 0, 0, 0 );
6875 }
6876
6877 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6878}
6879
6880static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6881{
6882 // Hue ranges from 0 - 360
6883 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6884 // Saturation ranges from 0 - 100
6885 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6886 // Value ranges from 0 - 100
6887 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6888 // Alpha ranges from 0 - 255
6889 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6890
6891 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6892 if ( !color.isValid() )
6893 {
6894 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6895 color = QColor( 0, 0, 0 );
6896 }
6897 return QgsSymbolLayerUtils::encodeColor( color );
6898}
6899
6900static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6901{
6902 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6903 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6904 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6905 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6906 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6907
6908 if ( !color.isValid() )
6909 {
6910 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6911 return QVariant();
6912 }
6913
6914 return color;
6915}
6916
6917static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6918{
6919 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6920 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6921 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6922 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6923 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6924
6925 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6926 if ( !color.isValid() )
6927 {
6928 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6929 return QVariant();
6930 }
6931
6932 return color;
6933}
6934
6935static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6936{
6937 // Cyan ranges from 0 - 100
6938 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6939 // Magenta ranges from 0 - 100
6940 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6941 // Yellow ranges from 0 - 100
6942 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6943 // Black ranges from 0 - 100
6944 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6945
6946 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6947
6948 if ( !color.isValid() )
6949 {
6950 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6951 color = QColor( 0, 0, 0 );
6952 }
6953
6954 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6955}
6956
6957static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6958{
6959 // Cyan ranges from 0 - 100
6960 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6961 // Magenta ranges from 0 - 100
6962 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6963 // Yellow ranges from 0 - 100
6964 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6965 // Black ranges from 0 - 100
6966 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6967 // Alpha ranges from 0 - 255
6968 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6969
6970 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6971 if ( !color.isValid() )
6972 {
6973 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6974 color = QColor( 0, 0, 0 );
6975 }
6976 return QgsSymbolLayerUtils::encodeColor( color );
6977}
6978
6979static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6980{
6981 const QVariant variant = values.at( 0 );
6982 bool isQColor;
6983 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6984 if ( !color.isValid() )
6985 return QVariant();
6986
6987 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6988 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6989 return color.red();
6990 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6991 return color.green();
6992 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6993 return color.blue();
6994 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6995 return color.alpha();
6996 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6997 return static_cast< double >( color.hsvHueF() * 360 );
6998 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6999 return static_cast< double >( color.hsvSaturationF() * 100 );
7000 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7001 return static_cast< double >( color.valueF() * 100 );
7002 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7003 return static_cast< double >( color.hslHueF() * 360 );
7004 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7005 return static_cast< double >( color.hslSaturationF() * 100 );
7006 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7007 return static_cast< double >( color.lightnessF() * 100 );
7008 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7009 return static_cast< double >( color.cyanF() * 100 );
7010 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7011 return static_cast< double >( color.magentaF() * 100 );
7012 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7013 return static_cast< double >( color.yellowF() * 100 );
7014 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7015 return static_cast< double >( color.blackF() * 100 );
7016
7017 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7018 return QVariant();
7019}
7020
7021static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7022{
7023 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7024 if ( map.empty() )
7025 {
7026 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
7027 return QVariant();
7028 }
7029
7030 QList< QColor > colors;
7032 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7033 {
7034 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
7035 if ( !colors.last().isValid() )
7036 {
7037 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
7038 return QVariant();
7039 }
7040
7041 double step = it.key().toDouble();
7042 if ( it == map.constBegin() )
7043 {
7044 if ( step != 0.0 )
7045 stops << QgsGradientStop( step, colors.last() );
7046 }
7047 else if ( it == map.constEnd() )
7048 {
7049 if ( step != 1.0 )
7050 stops << QgsGradientStop( step, colors.last() );
7051 }
7052 else
7053 {
7054 stops << QgsGradientStop( step, colors.last() );
7055 }
7056 }
7057 bool discrete = values.at( 1 ).toBool();
7058
7059 if ( colors.empty() )
7060 return QVariant();
7061
7062 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
7063}
7064
7065static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7066{
7067 const QVariant variant = values.at( 0 );
7068 bool isQColor;
7069 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7070 if ( !color.isValid() )
7071 return QVariant();
7072
7073 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7074 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7075 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
7076 color.setRed( std::clamp( value, 0, 255 ) );
7077 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
7078 color.setGreen( std::clamp( value, 0, 255 ) );
7079 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
7080 color.setBlue( std::clamp( value, 0, 255 ) );
7081 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
7082 color.setAlpha( std::clamp( value, 0, 255 ) );
7083 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
7084 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
7085 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
7086 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
7087 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
7088 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7089 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
7090 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
7091 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
7092 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
7093 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
7094 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7095 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
7096 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
7097 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
7098 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
7099 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
7100 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
7101 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
7102 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
7103 else
7104 {
7105 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
7106 return QVariant();
7107 }
7108 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7109}
7110
7111static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7112{
7113 const QVariant variant = values.at( 0 );
7114 bool isQColor;
7115 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7116 if ( !color.isValid() )
7117 return QVariant();
7118
7119 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7120
7121 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7122}
7123
7124static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7125{
7126 const QVariant variant = values.at( 0 );
7127 bool isQColor;
7128 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
7129 if ( !color.isValid() )
7130 return QVariant();
7131
7132 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
7133
7134 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
7135}
7136
7137static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7138{
7139 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7140 QgsGeometry geom = feat.geometry();
7141 if ( !geom.isNull() )
7142 return QVariant::fromValue( geom );
7143 return QVariant();
7144}
7145
7146static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7147{
7148 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
7149 if ( !feat.isValid() )
7150 return QVariant();
7151 return feat.id();
7152}
7153
7154static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7155{
7156 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
7157 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
7158 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
7159
7160 if ( !sCrs.isValid() )
7161 return QVariant::fromValue( fGeom );
7162
7163 if ( !dCrs.isValid() )
7164 return QVariant::fromValue( fGeom );
7165
7167 if ( context )
7168 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
7169 QgsCoordinateTransform t( sCrs, dCrs, tContext );
7170 try
7171 {
7173 return QVariant::fromValue( fGeom );
7174 }
7175 catch ( QgsCsException &cse )
7176 {
7177 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
7178 return QVariant();
7179 }
7180 return QVariant();
7181}
7182
7183
7184static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7185{
7186 bool foundLayer = false;
7187 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7188
7189 //no layer found
7190 if ( !featureSource || !foundLayer )
7191 {
7192 return QVariant();
7193 }
7194
7195 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
7196
7198 req.setFilterFid( fid );
7199 req.setTimeout( 10000 );
7200 req.setRequestMayBeNested( true );
7201 if ( context )
7202 req.setFeedback( context->feedback() );
7203 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7204
7205 QgsFeature fet;
7206 QVariant result;
7207 if ( fIt.nextFeature( fet ) )
7208 result = QVariant::fromValue( fet );
7209
7210 return result;
7211}
7212
7213static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7214{
7215 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
7216 bool foundLayer = false;
7217 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
7218
7219 //no layer found
7220 if ( !featureSource || !foundLayer )
7221 {
7222 return QVariant();
7223 }
7225 QString cacheValueKey;
7226 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7227 {
7228 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7229
7230 QMap<QString, QVariant>::const_iterator i = attributeMap.constBegin();
7231 QString filterString;
7232 for ( ; i != attributeMap.constEnd(); ++i )
7233 {
7234 if ( !filterString.isEmpty() )
7235 {
7236 filterString.append( " AND " );
7237 }
7238 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7239 }
7240 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7241 if ( context && context->hasCachedValue( cacheValueKey ) )
7242 {
7243 return context->cachedValue( cacheValueKey );
7244 }
7245 req.setFilterExpression( filterString );
7246 }
7247 else
7248 {
7249 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7250 int attributeId = featureSource->fields().lookupField( attribute );
7251 if ( attributeId == -1 )
7252 {
7253 return QVariant();
7254 }
7255
7256 const QVariant &attVal = values.at( 2 );
7257
7258 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7259 if ( context && context->hasCachedValue( cacheValueKey ) )
7260 {
7261 return context->cachedValue( cacheValueKey );
7262 }
7263
7265 }
7266 req.setLimit( 1 );
7267 req.setTimeout( 10000 );
7268 req.setRequestMayBeNested( true );
7269 if ( context )
7270 req.setFeedback( context->feedback() );
7271 if ( !parent->needsGeometry() )
7272 {
7274 }
7275 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7276
7277 QgsFeature fet;
7278 QVariant res;
7279 if ( fIt.nextFeature( fet ) )
7280 {
7281 res = QVariant::fromValue( fet );
7282 }
7283
7284 if ( context )
7285 context->setCachedValue( cacheValueKey, res );
7286 return res;
7287}
7288
7289static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7290{
7291 QVariant result;
7292 QString fieldName;
7293
7294 if ( context )
7295 {
7296 if ( !values.isEmpty() )
7297 {
7298 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7299 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7300 fieldName = col->name();
7301 else if ( values.size() == 2 )
7302 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7303 }
7304
7305 QVariant value = values.at( 0 );
7306
7307 const QgsFields fields = context->fields();
7308 int fieldIndex = fields.lookupField( fieldName );
7309
7310 if ( fieldIndex == -1 )
7311 {
7312 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7313 }
7314 else
7315 {
7316 // TODO this function is NOT thread safe
7318 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7320
7321 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7322 if ( context->hasCachedValue( cacheValueKey ) )
7323 {
7324 return context->cachedValue( cacheValueKey );
7325 }
7326
7327 if ( layer )
7328 {
7329 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7331
7332 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7333
7334 QVariant cache;
7335 if ( !context->hasCachedValue( cacheKey ) )
7336 {
7337 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7338 context->setCachedValue( cacheKey, cache );
7339 }
7340 else
7341 cache = context->cachedValue( cacheKey );
7342
7343 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7344 }
7345
7346 context->setCachedValue( cacheValueKey, result );
7347 }
7348 }
7349 else
7350 {
7351 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7352 }
7353
7354 return result;
7355}
7356
7357static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7358{
7359 const QVariant data = values.at( 0 );
7360 const QMimeDatabase db;
7361 return db.mimeTypeForData( data.toByteArray() ).name();
7362}
7363
7364static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7365{
7366 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7367
7368 bool translate = true;
7369 if ( values.length() >= 3 )
7370 {
7371 translate = QgsExpressionUtils::getTVLValue( values.at( 2 ), parent ) == QgsExpressionUtils::TVL::True;
7372 }
7373
7374 bool foundLayer = false;
7375 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7376 values.at( 0 ),
7377 context,
7378 parent,
7379 [layerProperty, translate]( QgsMapLayer *layer ) -> QVariant {
7380 if ( !layer )
7381 return QVariant();
7382
7383 // here, we always prefer the layer metadata values over the older server-specific published values
7384 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7385 return layer->name();
7386 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7387 return layer->id();
7388 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7389 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7390 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7391 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7392 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7393 {
7394 QStringList keywords;
7395 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7396 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7397 {
7398 keywords.append( it.value() );
7399 }
7400 if ( !keywords.isEmpty() )
7401 return keywords;
7402 return layer->serverProperties()->keywordList();
7403 }
7404 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7405 return layer->serverProperties()->dataUrl();
7406 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7407 {
7408 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7409 }
7410 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7411 return layer->serverProperties()->attributionUrl();
7412 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7413 return layer->publicSource();
7414 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7415 return layer->minimumScale();
7416 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7417 return layer->maximumScale();
7418 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7419 return layer->isEditable();
7420 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7421 return layer->crs().authid();
7422 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7423 return layer->crs().toProj();
7424 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7425 return layer->crs().description();
7426 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7427 return layer->crs().ellipsoidAcronym();
7428 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7429 {
7430 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7431 QVariant result = QVariant::fromValue( extentGeom );
7432 return result;
7433 }
7434 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7435 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7436 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7437 {
7438 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7439 return decodedUri.value( u"path"_s );
7440 }
7441 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7442 {
7443 switch ( layer->type() )
7444 {
7446 return translate ? QCoreApplication::translate( "expressions", "Vector" ) : u"Vector"_s;
7448 return translate ? QCoreApplication::translate( "expressions", "Raster" ) : u"Raster"_s;
7450 return translate ? QCoreApplication::translate( "expressions", "Mesh" ) : u"Mesh"_s;
7452 return translate ? QCoreApplication::translate( "expressions", "Vector Tile" ) : u"Vector Tile"_s;
7454 return translate ? QCoreApplication::translate( "expressions", "Plugin" ) : u"Plugin"_s;
7456 return translate ? QCoreApplication::translate( "expressions", "Annotation" ) : u"Annotation"_s;
7458 return translate ? QCoreApplication::translate( "expressions", "Point Cloud" ) : u"Point Cloud"_s;
7460 return translate ? QCoreApplication::translate( "expressions", "Group" ) : u"Group"_s;
7462 return translate ? QCoreApplication::translate( "expressions", "Tiled Scene" ) : u"Tiled Scene"_s;
7463 }
7464 }
7465 else
7466 {
7467 //vector layer methods
7468 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7469 if ( vLayer )
7470 {
7471 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7472 return vLayer->storageType();
7473 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7475 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7476 return QVariant::fromValue( vLayer->featureCount() );
7477 }
7478 }
7479
7480 return QVariant();
7481 },
7482 foundLayer
7483 );
7484
7485 if ( !foundLayer )
7486 return QVariant();
7487 else
7488 return res;
7489}
7490
7491static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7492{
7493 const QString uriPart = values.at( 1 ).toString();
7494
7495 bool foundLayer = false;
7496
7497 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7498 values.at( 0 ),
7499 context,
7500 parent,
7501 [parent, uriPart]( QgsMapLayer *layer ) -> QVariant {
7502 if ( !layer->dataProvider() )
7503 {
7504 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7505 return QVariant();
7506 }
7507
7508 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7509
7510 if ( !uriPart.isNull() )
7511 {
7512 return decodedUri.value( uriPart );
7513 }
7514 else
7515 {
7516 return decodedUri;
7517 }
7518 },
7519 foundLayer
7520 );
7521
7522 if ( !foundLayer )
7523 {
7524 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7525 return QVariant();
7526 }
7527 else
7528 {
7529 return res;
7530 }
7531}
7532
7533static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7534{
7535 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7536 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7537
7538 bool foundLayer = false;
7539 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7540 values.at( 0 ),
7541 context,
7542 parent,
7543 [parent, band, layerProperty]( QgsMapLayer *layer ) -> QVariant {
7544 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7545 if ( !rl )
7546 return QVariant();
7547
7548 if ( band < 1 || band > rl->bandCount() )
7549 {
7550 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7551 return QVariant();
7552 }
7553
7555
7556 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7558 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7560 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7562 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7564 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7566 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7568 else
7569 {
7570 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7571 return QVariant();
7572 }
7573
7574 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7575 switch ( stat )
7576 {
7578 return stats.mean;
7580 return stats.stdDev;
7582 return stats.minimumValue;
7584 return stats.maximumValue;
7586 return stats.range;
7588 return stats.sum;
7589 default:
7590 break;
7591 }
7592 return QVariant();
7593 },
7594 foundLayer
7595 );
7596
7597 if ( !foundLayer )
7598 {
7599#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7600 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7601#endif
7602 return QVariant();
7603 }
7604 else
7605 {
7606 return res;
7607 }
7608}
7609
7610static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7611{
7612 return values;
7613}
7614
7615static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7616{
7617 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7618 bool ascending = values.value( 1 ).toBool();
7619 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7620 return list;
7621}
7622
7623static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7624{
7625 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7626}
7627
7628static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7629{
7630 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7631}
7632
7633static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7634{
7635 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7636}
7637
7638static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7639{
7640 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7641 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7642 int match = 0;
7643 for ( const auto &item : listB )
7644 {
7645 if ( listA.contains( item ) )
7646 match++;
7647 }
7648
7649 return QVariant( match == listB.count() );
7650}
7651
7652static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7653{
7654 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7655}
7656
7657static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7658{
7659 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7660 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7661 if ( pos < list.length() && pos >= 0 )
7662 return list.at( pos );
7663 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7664 return list.at( list.length() + pos );
7665 return QVariant();
7666}
7667
7668static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7669{
7670 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7671 return list.value( 0 );
7672}
7673
7674static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7675{
7676 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7677 return list.value( list.size() - 1 );
7678}
7679
7680static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7681{
7682 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7683 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7684}
7685
7686static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7687{
7688 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7689 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7690}
7691
7692static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7693{
7694 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7695 int i = 0;
7696 double total = 0.0;
7697 for ( const QVariant &item : list )
7698 {
7699 switch ( item.userType() )
7700 {
7701 case QMetaType::Int:
7702 case QMetaType::UInt:
7703 case QMetaType::LongLong:
7704 case QMetaType::ULongLong:
7705 case QMetaType::Float:
7706 case QMetaType::Double:
7707 total += item.toDouble();
7708 ++i;
7709 break;
7710 }
7711 }
7712 return i == 0 ? QVariant() : total / i;
7713}
7714
7715static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7716{
7717 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7718 QVariantList numbers;
7719 for ( const auto &item : list )
7720 {
7721 switch ( item.userType() )
7722 {
7723 case QMetaType::Int:
7724 case QMetaType::UInt:
7725 case QMetaType::LongLong:
7726 case QMetaType::ULongLong:
7727 case QMetaType::Float:
7728 case QMetaType::Double:
7729 numbers.append( item );
7730 break;
7731 }
7732 }
7733 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7734 const int count = numbers.count();
7735 if ( count == 0 )
7736 {
7737 return QVariant();
7738 }
7739 else if ( count % 2 )
7740 {
7741 return numbers.at( count / 2 );
7742 }
7743 else
7744 {
7745 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7746 }
7747}
7748
7749static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7750{
7751 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7752 int i = 0;
7753 double total = 0.0;
7754 for ( const QVariant &item : list )
7755 {
7756 switch ( item.userType() )
7757 {
7758 case QMetaType::Int:
7759 case QMetaType::UInt:
7760 case QMetaType::LongLong:
7761 case QMetaType::ULongLong:
7762 case QMetaType::Float:
7763 case QMetaType::Double:
7764 total += item.toDouble();
7765 ++i;
7766 break;
7767 }
7768 }
7769 return i == 0 ? QVariant() : total;
7770}
7771
7772static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7773{
7774 QVariant result = value;
7775 ( void ) result.convert( static_cast<int>( type ) );
7776 return result;
7777}
7778
7779static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7780{
7781 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7782 QHash< QVariant, int > hash;
7783 for ( const auto &item : list )
7784 {
7785 ++hash[item];
7786 }
7787 const QList< int > occurrences = hash.values();
7788 if ( occurrences.empty() )
7789 return QVariantList();
7790
7791 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7792
7793 const QString option = values.at( 1 ).toString();
7794 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7795 {
7796 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7797 }
7798 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7799 {
7800 if ( hash.isEmpty() )
7801 return QVariant();
7802
7803 return QVariant( hash.key( maxValue ) );
7804 }
7805 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7806 {
7807 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7808 }
7809 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7810 {
7811 if ( maxValue * 2 <= list.size() )
7812 return QVariant();
7813
7814 return QVariant( hash.key( maxValue ) );
7815 }
7816 else
7817 {
7818 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7819 return QVariant();
7820 }
7821}
7822
7823static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7824{
7825 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7826 QHash< QVariant, int > hash;
7827 for ( const auto &item : list )
7828 {
7829 ++hash[item];
7830 }
7831 const QList< int > occurrences = hash.values();
7832 if ( occurrences.empty() )
7833 return QVariantList();
7834
7835 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7836
7837 const QString option = values.at( 1 ).toString();
7838 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7839 {
7840 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7841 }
7842 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7843 {
7844 if ( hash.isEmpty() )
7845 return QVariant();
7846
7847 return QVariant( hash.key( minValue ) );
7848 }
7849 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7850 {
7851 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7852 }
7853 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7854 {
7855 if ( hash.isEmpty() )
7856 return QVariant();
7857
7858 // Remove the majority, all others are minority
7859 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7860 if ( maxValue * 2 > list.size() )
7861 hash.remove( hash.key( maxValue ) );
7862
7863 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7864 }
7865 else
7866 {
7867 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7868 return QVariant();
7869 }
7870}
7871
7872static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7873{
7874 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7875 list.append( values.at( 1 ) );
7876 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7877}
7878
7879static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7880{
7881 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7882 list.prepend( values.at( 1 ) );
7883 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7884}
7885
7886static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7887{
7888 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7889 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7890 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7891}
7892
7893static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7894{
7895 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7896 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7897 if ( position < 0 )
7898 position = position + list.length();
7899 if ( position >= 0 && position < list.length() )
7900 list.removeAt( position );
7901 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7902}
7903
7904static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7905{
7906 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7907 return QVariant();
7908
7909 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7910
7911 const QVariant toRemove = values.at( 1 );
7912 if ( QgsVariantUtils::isNull( toRemove ) )
7913 {
7914 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant &element ) { return QgsVariantUtils::isNull( element ); } ), list.end() );
7915 }
7916 else
7917 {
7918 list.removeAll( toRemove );
7919 }
7920 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7921}
7922
7923static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7924{
7925 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7926 {
7927 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7928
7929 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7930 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7931 {
7932 int index = list.indexOf( it.key() );
7933 while ( index >= 0 )
7934 {
7935 list.replace( index, it.value() );
7936 index = list.indexOf( it.key() );
7937 }
7938 }
7939
7940 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7941 }
7942 else if ( values.count() == 3 )
7943 {
7944 QVariantList before;
7945 QVariantList after;
7946 bool isSingleReplacement = false;
7947
7948 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7949 {
7950 before = QVariantList() << values.at( 1 );
7951 }
7952 else
7953 {
7954 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7955 }
7956
7957 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7958 {
7959 after = QVariantList() << values.at( 2 );
7960 isSingleReplacement = true;
7961 }
7962 else
7963 {
7964 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7965 }
7966
7967 if ( !isSingleReplacement && before.length() != after.length() )
7968 {
7969 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7970 return QVariant();
7971 }
7972
7973 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7974 for ( int i = 0; i < before.length(); i++ )
7975 {
7976 int index = list.indexOf( before.at( i ) );
7977 while ( index >= 0 )
7978 {
7979 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7980 index = list.indexOf( before.at( i ) );
7981 }
7982 }
7983
7984 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7985 }
7986 else
7987 {
7988 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7989 return QVariant();
7990 }
7991}
7992
7993static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7994{
7995 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7996 QVariantList list_new;
7997
7998 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7999 {
8000 while ( list.removeOne( cur ) )
8001 {
8002 list_new.append( cur );
8003 }
8004 }
8005
8006 list_new.append( list );
8007
8008 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
8009}
8010
8011static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8012{
8013 QVariantList list;
8014 for ( const QVariant &cur : values )
8015 {
8016 list += QgsExpressionUtils::getListValue( cur, parent );
8017 }
8018 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
8019}
8020
8021static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8022{
8023 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8024 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
8025 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
8026 int slice_length = 0;
8027 // negative positions means positions taken relative to the end of the array
8028 if ( start_pos < 0 )
8029 {
8030 start_pos = list.length() + start_pos;
8031 }
8032 if ( end_pos >= 0 )
8033 {
8034 slice_length = end_pos - start_pos + 1;
8035 }
8036 else
8037 {
8038 slice_length = list.length() + end_pos - start_pos + 1;
8039 }
8040 //avoid negative lengths in QList.mid function
8041 if ( slice_length < 0 )
8042 {
8043 slice_length = 0;
8044 }
8045 list = list.mid( start_pos, slice_length );
8046 return list;
8047}
8048
8049static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8050{
8051 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8052 std::reverse( list.begin(), list.end() );
8053 return list;
8054}
8055
8056static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8057{
8058 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8059 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
8060 for ( const QVariant &cur : array2 )
8061 {
8062 if ( array1.contains( cur ) )
8063 return QVariant( true );
8064 }
8065 return QVariant( false );
8066}
8067
8068static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8069{
8070 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8071
8072 QVariantList distinct;
8073
8074 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8075 {
8076 if ( !distinct.contains( *it ) )
8077 {
8078 distinct += ( *it );
8079 }
8080 }
8081
8082 return distinct;
8083}
8084
8085static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8086{
8087 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
8088 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8089 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8090
8091 QString str;
8092
8093 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8094 {
8095 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
8096 if ( it != ( array.constEnd() - 1 ) )
8097 {
8098 str += delimiter;
8099 }
8100 }
8101
8102 return QVariant( str );
8103}
8104
8105static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8106{
8107 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8108 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8109 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
8110
8111 QStringList list = str.split( delimiter );
8112 QVariantList array;
8113
8114 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
8115 {
8116 array += ( !( *it ).isEmpty() ) ? *it : empty;
8117 }
8118
8119 return array;
8120}
8121
8122static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8123{
8124 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8125 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
8126 if ( document.isNull() )
8127 return QVariant();
8128
8129 return document.toVariant();
8130}
8131
8132static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8133{
8134 Q_UNUSED( parent )
8135 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
8136 return QString( document.toJson( QJsonDocument::Compact ) );
8137}
8138
8139static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8140{
8141 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8142 if ( str.isEmpty() )
8143 return QVariantMap();
8144 str = str.trimmed();
8145
8146 return QgsHstoreUtils::parse( str );
8147}
8148
8149static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8150{
8151 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8152 return QgsHstoreUtils::build( map );
8153}
8154
8155static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8156{
8157 QVariantMap result;
8158 for ( int i = 0; i + 1 < values.length(); i += 2 )
8159 {
8160 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
8161 }
8162 return result;
8163}
8164
8165static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8166{
8167 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8168 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
8169 QVariantMap resultMap;
8170
8171 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8172 {
8173 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
8174 }
8175
8176 return resultMap;
8177}
8178
8179static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8180{
8181 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
8182}
8183
8184static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8185{
8186 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
8187}
8188
8189static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8190{
8191 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8192 map.remove( values.at( 1 ).toString() );
8193 return map;
8194}
8195
8196static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8197{
8198 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8199 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
8200 return map;
8201}
8202
8203static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8204{
8205 QVariantMap result;
8206 for ( const QVariant &cur : values )
8207 {
8208 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
8209 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
8210 result.insert( it.key(), it.value() );
8211 }
8212 return result;
8213}
8214
8215static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8216{
8217 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
8218}
8219
8220static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8221{
8222 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
8223}
8224
8225static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8226{
8227 const QString envVarName = values.at( 0 ).toString();
8228 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
8229 return QVariant();
8230
8231 return QProcessEnvironment::systemEnvironment().value( envVarName );
8232}
8233
8234static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8235{
8236 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8237 if ( parent->hasEvalError() )
8238 {
8239 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "base_file_name"_L1 ) );
8240 return QVariant();
8241 }
8242 return QFileInfo( file ).completeBaseName();
8243}
8244
8245static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8246{
8247 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8248 if ( parent->hasEvalError() )
8249 {
8250 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_suffix"_L1 ) );
8251 return QVariant();
8252 }
8253 return QFileInfo( file ).completeSuffix();
8254}
8255
8256static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8257{
8258 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8259 if ( parent->hasEvalError() )
8260 {
8261 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8262 return QVariant();
8263 }
8264 return QFileInfo::exists( file );
8265}
8266
8267static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8268{
8269 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8270 if ( parent->hasEvalError() )
8271 {
8272 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_name"_L1 ) );
8273 return QVariant();
8274 }
8275 return QFileInfo( file ).fileName();
8276}
8277
8278static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8279{
8280 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8281 if ( parent->hasEvalError() )
8282 {
8283 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_file"_L1 ) );
8284 return QVariant();
8285 }
8286 return QFileInfo( file ).isFile();
8287}
8288
8289static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8290{
8291 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8292 if ( parent->hasEvalError() )
8293 {
8294 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8295 return QVariant();
8296 }
8297 return QFileInfo( file ).isDir();
8298}
8299
8300static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8301{
8302 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8303 if ( parent->hasEvalError() )
8304 {
8305 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8306 return QVariant();
8307 }
8308 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8309}
8310
8311static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8312{
8313 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8314 if ( parent->hasEvalError() )
8315 {
8316 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8317 return QVariant();
8318 }
8319 return QFileInfo( file ).size();
8320}
8321
8322static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8323{
8324 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8325}
8326
8327static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8328{
8329 QVariant hash;
8330 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8331 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8332
8333 if ( method == "md4"_L1 )
8334 {
8335 hash = fcnHash( str, QCryptographicHash::Md4 );
8336 }
8337 else if ( method == "md5"_L1 )
8338 {
8339 hash = fcnHash( str, QCryptographicHash::Md5 );
8340 }
8341 else if ( method == "sha1"_L1 )
8342 {
8343 hash = fcnHash( str, QCryptographicHash::Sha1 );
8344 }
8345 else if ( method == "sha224"_L1 )
8346 {
8347 hash = fcnHash( str, QCryptographicHash::Sha224 );
8348 }
8349 else if ( method == "sha256"_L1 )
8350 {
8351 hash = fcnHash( str, QCryptographicHash::Sha256 );
8352 }
8353 else if ( method == "sha384"_L1 )
8354 {
8355 hash = fcnHash( str, QCryptographicHash::Sha384 );
8356 }
8357 else if ( method == "sha512"_L1 )
8358 {
8359 hash = fcnHash( str, QCryptographicHash::Sha512 );
8360 }
8361 else if ( method == "sha3_224"_L1 )
8362 {
8363 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8364 }
8365 else if ( method == "sha3_256"_L1 )
8366 {
8367 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8368 }
8369 else if ( method == "sha3_384"_L1 )
8370 {
8371 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8372 }
8373 else if ( method == "sha3_512"_L1 )
8374 {
8375 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8376 }
8377 else if ( method == "keccak_224"_L1 )
8378 {
8379 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8380 }
8381 else if ( method == "keccak_256"_L1 )
8382 {
8383 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8384 }
8385 else if ( method == "keccak_384"_L1 )
8386 {
8387 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8388 }
8389 else if ( method == "keccak_512"_L1 )
8390 {
8391 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8392 }
8393 else
8394 {
8395 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8396 }
8397 return hash;
8398}
8399
8400static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8401{
8402 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8403}
8404
8405static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8406{
8407 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8408}
8409
8410static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8411{
8412 const QByteArray input = values.at( 0 ).toByteArray();
8413 return QVariant( QString( input.toBase64() ) );
8414}
8415
8416static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8417{
8418 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8419 QUrlQuery query;
8420 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8421 {
8422 query.addQueryItem( it.key(), it.value().toString() );
8423 }
8424 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8426
8427static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8428{
8429 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8430 const QByteArray base64 = value.toLocal8Bit();
8431 const QByteArray decoded = QByteArray::fromBase64( base64 );
8432 return QVariant( decoded );
8433}
8434
8436typedef std::function<bool( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &values, Qgis::GeometryBackend backend )> RelationFunction;
8437
8438static QVariant executeGeomOverlay(
8439 const QVariantList &values,
8440 const QgsExpressionContext *context,
8441 QgsExpression *parent,
8442 const RelationFunction &relationFunction,
8443 bool invert = false,
8444 double bboxGrow = 0,
8445 bool isNearestFunc = false,
8446 bool isIntersectsFunc = false
8447)
8448{
8449 if ( !context )
8450 {
8451 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8452 return QVariant();
8453 }
8454
8455 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8456 // TODO this function is NOT thread safe
8458 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8460
8461 QgsFeatureRequest request;
8462 request.setTimeout( 10000 );
8463 request.setRequestMayBeNested( true );
8464 request.setFeedback( context->feedback() );
8465
8466 // First parameter is the overlay layer
8467 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8469
8470 const bool layerCanBeCached = node->isStatic( parent, context );
8471 QVariant targetLayerValue = node->eval( parent, context );
8473
8474 // Second parameter is the expression to evaluate (or null for testonly)
8475 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8477 QString subExpString = node->dump();
8478
8479 bool testOnly = ( subExpString == "NULL" );
8480 // TODO this function is NOT thread safe
8482 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8484 if ( !targetLayer ) // No layer, no joy
8485 {
8486 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8487 return QVariant();
8488 }
8489
8490 // Third parameter is the filtering expression
8491 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8493 QString filterString = node->dump();
8494 if ( filterString != "NULL" )
8495 {
8496 request.setFilterExpression( filterString ); //filter cached features
8497 }
8498
8499 // Fourth parameter is the limit
8500 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8502 QVariant limitValue = node->eval( parent, context );
8504 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8505
8506 double max_distance = 0;
8507 bool cacheEnabled = false;
8508
8509 double minOverlap { -1 };
8510 double minInscribedCircleRadius { -1 };
8511 bool returnDetails = false; //#spellok
8512 bool sortByMeasure = false;
8513 bool sortAscending = false;
8514 bool requireMeasures = false;
8515 bool overlapOrRadiusFilter = false;
8516
8518
8519 if ( isNearestFunc ) //maxdistance param handling
8520 {
8521 // Fifth parameter (for nearest only) is the max distance
8522 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8524 QVariant distanceValue = node->eval( parent, context );
8526 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8527
8528 // Sixth (for nearest only) parameter is the cache toggle
8529 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
8531 QVariant cacheValue = node->eval( parent, context );
8533 cacheEnabled = cacheValue.toBool();
8534 }
8535 else
8536 {
8537 // Fifth parameter is the cache toggle
8538 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8540 QVariant cacheValue = node->eval( parent, context );
8542 cacheEnabled = cacheValue.toBool();
8543
8544 // Sixth parameter is the min overlap (area or length)
8545 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8547 const QVariant minOverlapValue = node->eval( parent, context );
8549 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8550
8551 // Seventh parameter is the min inscribed circle radius
8552 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8554 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8556 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8557
8558 // Eighth parameter is the return_details
8559 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8560 // Return measures is only effective when an expression is set
8561 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8562
8563 // Ninth parameter is the sort_by_intersection_size flag
8564 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8565 // Sort by measures is only effective when an expression is set
8566 const QString sorting { node->eval( parent, context ).toString().toLower() };
8567 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8568 sortAscending = sorting.startsWith( "asc" );
8569 requireMeasures = sortByMeasure || returnDetails; //#spellok
8570 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8571
8572 // Tenth parameter is the geometry backend
8573 node = QgsExpressionUtils::getNode( values.at( 9 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8575 const QString backendStr = node->eval( parent, context ).toString().toUpper();
8577
8578 bool ok;
8579 backend = qgsEnumKeyToValue( backendStr, Qgis::GeometryBackend::GEOS, false, &ok );
8580 if ( !ok )
8581 SET_EVAL_ERROR( u"Geometry backend '%1' does not exist!"_s.arg( backendStr ) );
8582 }
8583
8584 FEAT_FROM_CONTEXT( context, feat )
8585 const QgsGeometry geometry = feat.geometry();
8586
8587 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8588 {
8589 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8590 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8591 }
8592
8593 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8594
8595 QgsRectangle intDomain = geometry.boundingBox();
8596 if ( bboxGrow != 0 )
8597 {
8598 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8599 }
8600
8601 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8602
8603 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8604 // Otherwise, it can be toggled by the user
8605 QgsSpatialIndex spatialIndex;
8606 QgsVectorLayer *cachedTarget;
8607 QList<QgsFeature> features;
8608 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8609 {
8610 // If the cache (local spatial index) is enabled, we materialize the whole
8611 // layer, then do the request on that layer instead.
8612 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8613 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8614
8615 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8616 {
8617 cachedTarget = targetLayer->materialize( request );
8618 if ( layerCanBeCached )
8619 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8620 }
8621 else
8622 {
8623 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8624 }
8625
8626 if ( !context->hasCachedValue( cacheIndex ) )
8627 {
8628 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8629 if ( layerCanBeCached )
8630 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8631 }
8632 else
8633 {
8634 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8635 }
8636
8637 QList<QgsFeatureId> fidsList;
8638 if ( isNearestFunc )
8639 {
8640 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8641 }
8642 else
8643 {
8644 fidsList = spatialIndex.intersects( intDomain );
8645 }
8646
8647 QListIterator<QgsFeatureId> i( fidsList );
8648 while ( i.hasNext() )
8649 {
8650 QgsFeatureId fId2 = i.next();
8651 if ( sameLayers && feat.id() == fId2 )
8652 continue;
8653 features.append( cachedTarget->getFeature( fId2 ) );
8654 }
8655 }
8656 else
8657 {
8658 // If the cache (local spatial index) is not enabled, we directly
8659 // get the features from the target layer
8660 request.setFilterRect( intDomain );
8661 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8662 QgsFeature feat2;
8663 while ( fit.nextFeature( feat2 ) )
8664 {
8665 if ( sameLayers && feat.id() == feat2.id() )
8666 continue;
8667 features.append( feat2 );
8668 }
8669 }
8670
8671 QgsExpression subExpression;
8672 QgsExpressionContext subContext;
8673 if ( !testOnly )
8674 {
8675 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8676 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8677
8678 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8679 {
8680 subExpression = QgsExpression( subExpString );
8682 subExpression.prepare( &subContext );
8683 }
8684 else
8685 {
8686 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8687 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8688 }
8689 }
8690
8691 // //////////////////////////////////////////////////////////////////
8692 // Helper functions for geometry tests
8693
8694 // Test function for linestring geometries, returns TRUE if test passes
8695 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double &overlapValue ) -> bool {
8696 bool testResult { false };
8697 // For return measures:
8698 QVector<double> overlapValues;
8699 const QgsGeometry merged { intersection.mergeLines() };
8700 for ( auto it = merged.const_parts_begin(); !testResult && it != merged.const_parts_end(); ++it )
8701 {
8703 // Check min overlap for intersection (if set)
8704 if ( minOverlap != -1 || requireMeasures )
8705 {
8706 overlapValue = geom->length();
8707 overlapValues.append( overlapValue );
8708 if ( minOverlap != -1 )
8709 {
8710 if ( overlapValue >= minOverlap )
8711 {
8712 testResult = true;
8713 }
8714 else
8715 {
8716 continue;
8717 }
8718 }
8719 }
8720 }
8721
8722 if ( !overlapValues.isEmpty() )
8723 {
8724 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8725 }
8726
8727 return testResult;
8728 };
8729
8730 // Test function for polygon geometries, returns TRUE if test passes
8731 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double &radiusValue, double &overlapValue ) -> bool {
8732 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8733 bool testResult { false };
8734 // For return measures:
8735 QVector<double> overlapValues;
8736 QVector<double> radiusValues;
8737 for ( auto it = intersection.const_parts_begin(); ( !testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8738 {
8740 // Check min overlap for intersection (if set)
8741 if ( minOverlap != -1 || requireMeasures )
8742 {
8743 overlapValue = geom->area();
8744 overlapValues.append( geom->area() );
8745 if ( minOverlap != -1 )
8746 {
8747 if ( overlapValue >= minOverlap )
8748 {
8749 testResult = true;
8750 }
8751 else
8752 {
8753 continue;
8754 }
8755 }
8756 }
8757
8758 // Check min inscribed circle radius for intersection (if set)
8759 if ( minInscribedCircleRadius != -1 || requireMeasures )
8760 {
8761 const QgsRectangle bbox = geom->boundingBox();
8762 const double width = bbox.width();
8763 const double height = bbox.height();
8764 const double size = width > height ? width : height;
8765 const double tolerance = size / 100.0;
8766 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8767 testResult = radiusValue >= minInscribedCircleRadius;
8768 radiusValues.append( radiusValues );
8769 }
8770 } // end for parts
8771
8772 // Get the max values
8773 if ( !radiusValues.isEmpty() )
8774 {
8775 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8776 }
8777
8778 if ( !overlapValues.isEmpty() )
8779 {
8780 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8781 }
8782
8783 return testResult;
8784 };
8785
8786
8787 bool found = false;
8788 int foundCount = 0;
8789 QVariantList results;
8790
8791 QListIterator<QgsFeature> i( features );
8792 try
8793 {
8794 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8795 {
8796 QgsFeature feat2 = i.next();
8797
8798
8799 if ( relationFunction( geometry, feat2.geometry(), values, backend ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8800 {
8801 double overlapValue = -1;
8802 double radiusValue = -1;
8803
8804 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8805 {
8806 QgsGeometry intersection { geometry.intersection( feat2.geometry(), QgsGeometryParameters() ) };
8807
8808 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8809 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8810 {
8811 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8812 intersection = QgsGeometry();
8813 QgsMultiPolygonXY poly;
8814 QgsMultiPolylineXY line;
8815 QgsMultiPointXY point;
8816 for ( const auto &geom : std::as_const( geometries ) )
8817 {
8818 switch ( geom.type() )
8819 {
8821 {
8822 poly.append( geom.asPolygon() );
8823 break;
8824 }
8826 {
8827 line.append( geom.asPolyline() );
8828 break;
8829 }
8831 {
8832 point.append( geom.asPoint() );
8833 break;
8834 }
8837 {
8838 break;
8839 }
8840 }
8841 }
8842
8843 switch ( geometry.type() )
8844 {
8846 {
8847 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8848 break;
8849 }
8851 {
8852 intersection = QgsGeometry::fromMultiPolylineXY( line );
8853 break;
8854 }
8856 {
8857 intersection = QgsGeometry::fromMultiPointXY( point );
8858 break;
8859 }
8862 {
8863 break;
8864 }
8865 }
8866 }
8867
8868 // Depending on the intersection geometry type and on the geometry type of
8869 // the tested geometry we can run different tests and collect different measures
8870 // that can be used for sorting (if required).
8871 switch ( intersection.type() )
8872 {
8874 {
8875 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8876 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8877
8878 if ( !testResult && overlapOrRadiusFilter )
8879 {
8880 continue;
8881 }
8882
8883 break;
8884 }
8885
8887 {
8888 // If the intersection is a linestring and a minimum circle is required
8889 // we can discard this result immediately.
8890 if ( minInscribedCircleRadius != -1 )
8891 {
8892 continue;
8893 }
8894
8895 // Otherwise a test for the overlap value is performed.
8896 const bool testResult { testLinestring( intersection, overlapValue ) };
8897
8898 if ( !testResult && overlapOrRadiusFilter )
8899 {
8900 continue;
8901 }
8902
8903 break;
8904 }
8905
8907 {
8908 // If the intersection is a point and a minimum circle is required
8909 // we can discard this result immediately.
8910 if ( minInscribedCircleRadius != -1 )
8911 {
8912 continue;
8913 }
8914
8915 bool testResult { false };
8916 if ( minOverlap != -1 || requireMeasures )
8917 {
8918 // Initially set this to 0 because it's a point intersection...
8919 overlapValue = 0;
8920 // ... but if the target geometry is not a point and the source
8921 // geometry is a point, we must record the length or the area
8922 // of the intersected geometry and use that as a measure for
8923 // sorting or reporting.
8924 if ( geometry.type() == Qgis::GeometryType::Point )
8925 {
8926 switch ( feat2.geometry().type() )
8927 {
8931 {
8932 break;
8933 }
8935 {
8936 testResult = testLinestring( feat2.geometry(), overlapValue );
8937 break;
8938 }
8940 {
8941 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8942 break;
8943 }
8944 }
8945 }
8946
8947 if ( !testResult && overlapOrRadiusFilter )
8948 {
8949 continue;
8950 }
8951 }
8952 break;
8953 }
8954
8957 {
8958 continue;
8959 }
8960 }
8961 }
8962
8963 found = true;
8964 foundCount++;
8965
8966 // We just want a single boolean result if there is any intersect: finish and return true
8967 if ( testOnly )
8968 break;
8969
8970 if ( !invert )
8971 {
8972 // We want a list of attributes / geometries / other expression values, evaluate now
8973 subContext.setFeature( feat2 );
8974 const QVariant expResult = subExpression.evaluate( &subContext );
8975
8976 if ( requireMeasures )
8977 {
8978 QVariantMap resultRecord;
8979 resultRecord.insert( u"id"_s, feat2.id() );
8980 resultRecord.insert( u"result"_s, expResult );
8981 // Overlap is always added because return measures was set
8982 resultRecord.insert( u"overlap"_s, overlapValue );
8983 // Radius is only added when is different than -1 (because for linestrings is not set)
8984 if ( radiusValue != -1 )
8985 {
8986 resultRecord.insert( u"radius"_s, radiusValue );
8987 }
8988 results.append( resultRecord );
8989 }
8990 else
8991 {
8992 results.append( expResult );
8993 }
8994 }
8995 else
8996 {
8997 // If not, results is a list of found ids, which we'll inverse and evaluate below
8998 results.append( feat2.id() );
8999 }
9000 }
9001 }
9002 }
9003 catch ( QgsException &e )
9004 {
9005 parent->setEvalErrorString( e.what() );
9006 return false;
9007 }
9008
9009 if ( testOnly )
9010 {
9011 if ( invert )
9012 found = !found; //for disjoint condition
9013 return found;
9014 }
9015
9016 if ( !invert )
9017 {
9018 if ( requireMeasures )
9019 {
9020 if ( sortByMeasure )
9021 {
9022 std::sort( results.begin(), results.end(), [sortAscending]( const QVariant &recordA, const QVariant &recordB ) -> bool {
9023 return sortAscending ? recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
9024 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
9025 } );
9026 }
9027 // Resize
9028 if ( limit > 0 && results.size() > limit )
9029 {
9030 results.erase( results.begin() + limit );
9031 }
9032
9033 if ( !returnDetails ) //#spellok
9034 {
9035 QVariantList expResults;
9036 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
9037 {
9038 expResults.append( it->toMap().value( u"result"_s ) );
9039 }
9040 return expResults;
9041 }
9042 }
9043
9044 return results;
9045 }
9046
9047 // for disjoint condition returns the results for cached layers not intersected feats
9048 QVariantList disjoint_results;
9049 QgsFeature feat2;
9050 QgsFeatureRequest request2;
9051 request2.setLimit( limit );
9052 if ( context )
9053 request2.setFeedback( context->feedback() );
9054 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
9055 while ( fi.nextFeature( feat2 ) )
9056 {
9057 if ( !results.contains( feat2.id() ) )
9058 {
9059 subContext.setFeature( feat2 );
9060 disjoint_results.append( subExpression.evaluate( &subContext ) );
9061 }
9062 }
9063 return disjoint_results;
9064}
9065
9066// Intersect functions:
9067
9068static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9069{
9070 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9071 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, false, true );
9072}
9073
9074static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9075{
9076 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.contains( other ); };
9077 return executeGeomOverlay( values, context, parent, geomFunction );
9078}
9079
9080static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9081{
9082 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.crosses( other ); };
9083 return executeGeomOverlay( values, context, parent, geomFunction );
9084}
9085
9086static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9087{
9088 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9089 return geometry.isExactlyEqual( other, Qgis::GeometryBackend::QGIS );
9090 };
9091 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9092}
9093
9094static QVariant fcnGeomOverlayEqualsExact( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9095{
9096 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9097 return geometry.isExactlyEqual( other, backend );
9098 };
9099 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9100}
9101
9102static QVariant fcnGeomOverlayEqualsTopological( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9103{
9104 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9105 return geometry.isTopologicallyEqual( other, backend );
9106 };
9107 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9108}
9109
9110static QVariant fcnGeomOverlayEqualsFuzzy( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9111{
9112 // This parameter is the epsilon tolerance
9113 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 10 ), parent );
9115 QVariant epsilonValue = node->eval( parent, context );
9117 double epsilon = QgsExpressionUtils::getDoubleValue( epsilonValue, parent );
9118
9119 RelationFunction geomFunction = [epsilon]( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend backend ) -> bool {
9120 return geometry.isFuzzyEqual( other, epsilon, backend );
9121 };
9122 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9123}
9124
9125static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9126{
9127 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.touches( other ); };
9128 return executeGeomOverlay( values, context, parent, geomFunction, false, 0.01 ); //grow amount should adapt to current units
9129}
9130
9131static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9132{
9133 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.within( other ); };
9134 return executeGeomOverlay( values, context, parent, geomFunction );
9135}
9136
9137static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9138{
9139 RelationFunction geomFunction = []( const QgsGeometry &geometry, const QgsGeometry &other, const QVariantList &, Qgis::GeometryBackend ) -> bool { return geometry.intersects( other ); };
9140 return executeGeomOverlay( values, context, parent, geomFunction, true, 0, false, true );
9141}
9142
9143static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
9144{
9145 RelationFunction geomFunction = []( const QgsGeometry &, const QgsGeometry &, const QVariantList &, Qgis::GeometryBackend ) -> bool {
9146 return true; // does nothing on purpose
9147 };
9148 return executeGeomOverlay( values, context, parent, geomFunction, false, 0, true );
9149}
9150
9151const QList<QgsExpressionFunction *> &QgsExpression::Functions()
9152{
9153 // The construction of the list isn't thread-safe, and without the mutex,
9154 // crashes in the WFS provider may occur, since it can parse expressions
9155 // in parallel.
9156 // The mutex needs to be recursive.
9157 QMutexLocker locker( &sFunctionsMutex );
9158
9159 QList<QgsExpressionFunction *> &functions = *sFunctions();
9160
9161 if ( functions.isEmpty() )
9162 {
9164 << QgsExpressionFunction::Parameter( u"expression"_s )
9165 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
9166 << QgsExpressionFunction::Parameter( u"filter"_s, true );
9167
9168 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
9169 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true ) << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9170
9171 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
9172 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
9173
9174 functions
9175 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
9176 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
9177 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
9178 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnAzimuth, u"GeometryGroup"_s )
9179 << new QgsStaticExpressionFunction(
9180 u"bearing"_s,
9182 << QgsExpressionFunction::Parameter( u"point1"_s )
9183 << QgsExpressionFunction::Parameter( u"point2"_s )
9184 << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() )
9185 << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ),
9186 fcnBearing,
9187 u"GeometryGroup"_s
9188 )
9189 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnInclination, u"GeometryGroup"_s )
9190 << new QgsStaticExpressionFunction(
9191 u"project"_s,
9193 << QgsExpressionFunction::Parameter( u"point"_s )
9194 << QgsExpressionFunction::Parameter( u"distance"_s )
9195 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9196 << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ),
9197 fcnProject,
9198 u"GeometryGroup"_s
9199 )
9200 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
9201 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
9202 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
9203 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
9204 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
9205 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
9206 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
9207 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
9208 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
9209 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
9210 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
9211 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
9212 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
9213
9214 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction(
9215 u"rand"_s,
9216 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9217 fcnRnd,
9218 u"Math"_s
9219 );
9220 randFunc->setIsStatic( false );
9221 functions << randFunc;
9222
9223 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction(
9224 u"randf"_s,
9226 << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 )
9227 << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 )
9228 << QgsExpressionFunction::Parameter( u"seed"_s, true ),
9229 fcnRndF,
9230 u"Math"_s
9231 );
9232 randfFunc->setIsStatic( false );
9233 functions << randfFunc;
9234
9235 functions
9236 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9237 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
9238 << 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 )
9239 << new QgsStaticExpressionFunction(
9240 u"scale_linear"_s,
9242 << QgsExpressionFunction::Parameter( u"value"_s )
9243 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9244 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9245 << QgsExpressionFunction::Parameter( u"range_min"_s )
9246 << QgsExpressionFunction::Parameter( u"range_max"_s ),
9247 fcnLinearScale,
9248 u"Math"_s
9249 )
9250 << new QgsStaticExpressionFunction(
9251 u"scale_polynomial"_s,
9253 << QgsExpressionFunction::Parameter( u"value"_s )
9254 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9255 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9256 << QgsExpressionFunction::Parameter( u"range_min"_s )
9257 << QgsExpressionFunction::Parameter( u"range_max"_s )
9258 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9259 fcnPolynomialScale,
9260 u"Math"_s,
9261 QString(),
9262 false,
9263 QSet<QString>(),
9264 false,
9265 QStringList() << u"scale_exp"_s
9266 )
9267 << new QgsStaticExpressionFunction(
9268 u"scale_exponential"_s,
9270 << QgsExpressionFunction::Parameter( u"value"_s )
9271 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9272 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9273 << QgsExpressionFunction::Parameter( u"range_min"_s )
9274 << QgsExpressionFunction::Parameter( u"range_max"_s )
9275 << QgsExpressionFunction::Parameter( u"exponent"_s ),
9276 fcnExponentialScale,
9277 u"Math"_s
9278 )
9279 << new QgsStaticExpressionFunction(
9280 u"scale_cubic_bezier"_s,
9282 << QgsExpressionFunction::Parameter( u"value"_s )
9283 << QgsExpressionFunction::Parameter( u"domain_min"_s )
9284 << QgsExpressionFunction::Parameter( u"domain_max"_s )
9285 << QgsExpressionFunction::Parameter( u"range_min"_s )
9286 << QgsExpressionFunction::Parameter( u"range_max"_s )
9287 << QgsExpressionFunction::Parameter( u"x1"_s )
9288 << QgsExpressionFunction::Parameter( u"y1"_s )
9289 << QgsExpressionFunction::Parameter( u"x2"_s )
9290 << QgsExpressionFunction::Parameter( u"y2"_s ),
9291 fcnCubicBezierScale,
9292 u"Math"_s
9293 )
9294 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
9295 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
9296 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
9297 << 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 )
9298 << 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 )
9299 << 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 )
9300 << new QgsStaticExpressionFunction(
9301 u"to_string"_s,
9302 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9303 fcnToString,
9304 QStringList() << u"Conversions"_s << u"String"_s,
9305 QString(),
9306 false,
9307 QSet<QString>(),
9308 false,
9309 QStringList() << u"tostring"_s
9310 )
9311 << new QgsStaticExpressionFunction(
9312 u"to_datetime"_s,
9314 << QgsExpressionFunction::Parameter( u"value"_s )
9315 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9316 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9317 fcnToDateTime,
9318 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9319 QString(),
9320 false,
9321 QSet<QString>(),
9322 false,
9323 QStringList() << u"todatetime"_s
9324 )
9325 << new QgsStaticExpressionFunction(
9326 u"to_date"_s,
9328 << QgsExpressionFunction::Parameter( u"value"_s )
9329 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9330 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9331 fcnToDate,
9332 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9333 QString(),
9334 false,
9335 QSet<QString>(),
9336 false,
9337 QStringList() << u"todate"_s
9338 )
9339 << new QgsStaticExpressionFunction(
9340 u"to_time"_s,
9342 << QgsExpressionFunction::Parameter( u"value"_s )
9343 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9344 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9345 fcnToTime,
9346 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9347 QString(),
9348 false,
9349 QSet<QString>(),
9350 false,
9351 QStringList() << u"totime"_s
9352 )
9353 << new QgsStaticExpressionFunction(
9354 u"to_interval"_s,
9355 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9356 fcnToInterval,
9357 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9358 QString(),
9359 false,
9360 QSet<QString>(),
9361 false,
9362 QStringList() << u"tointerval"_s
9363 )
9364 << new QgsStaticExpressionFunction(
9365 u"to_dm"_s,
9367 << QgsExpressionFunction::Parameter( u"value"_s )
9368 << QgsExpressionFunction::Parameter( u"axis"_s )
9369 << QgsExpressionFunction::Parameter( u"precision"_s )
9370 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9371 fcnToDegreeMinute,
9372 u"Conversions"_s,
9373 QString(),
9374 false,
9375 QSet<QString>(),
9376 false,
9377 QStringList() << u"todm"_s
9378 )
9379 << new QgsStaticExpressionFunction(
9380 u"to_dms"_s,
9382 << QgsExpressionFunction::Parameter( u"value"_s )
9383 << QgsExpressionFunction::Parameter( u"axis"_s )
9384 << QgsExpressionFunction::Parameter( u"precision"_s )
9385 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9386 fcnToDegreeMinuteSecond,
9387 u"Conversions"_s,
9388 QString(),
9389 false,
9390 QSet<QString>(),
9391 false,
9392 QStringList() << u"todms"_s
9393 )
9394 << 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 )
9395 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
9396 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
9397 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
9398 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9399 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
9400 << new QgsStaticExpressionFunction(
9401 u"if"_s,
9403 << QgsExpressionFunction::Parameter( u"condition"_s )
9404 << QgsExpressionFunction::Parameter( u"result_when_true"_s )
9405 << QgsExpressionFunction::Parameter( u"result_when_false"_s ),
9406 fcnIf,
9407 u"Conditionals"_s,
9408 QString(),
9409 false,
9410 QSet<QString>(),
9411 true
9412 )
9413 << new QgsStaticExpressionFunction(
9414 u"try"_s,
9415 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ),
9416 fcnTry,
9417 u"Conditionals"_s,
9418 QString(),
9419 false,
9420 QSet<QString>(),
9421 true
9422 )
9423
9424 << new QgsStaticExpressionFunction(
9425 u"aggregate"_s,
9427 << QgsExpressionFunction::Parameter( u"layer"_s )
9428 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9429 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9430 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9431 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9432 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9433 fcnAggregate,
9434 u"Aggregates"_s,
9435 QString(),
9436 []( const QgsExpressionNodeFunction *node ) {
9437 // usesGeometry callback: return true if @parent variable is referenced
9438
9439 if ( !node )
9440 return true;
9441
9442 if ( !node->args() )
9443 return false;
9444
9445 QSet<QString> referencedVars;
9446 if ( node->args()->count() > 2 )
9447 {
9448 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9449 referencedVars = subExpressionNode->referencedVariables();
9450 }
9451
9452 if ( node->args()->count() > 3 )
9453 {
9454 QgsExpressionNode *filterNode = node->args()->at( 3 );
9455 referencedVars.unite( filterNode->referencedVariables() );
9456 }
9457 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
9458 },
9459 []( const QgsExpressionNodeFunction *node ) {
9460 // referencedColumns callback: return AllAttributes if @parent variable is referenced
9461
9462 if ( !node )
9463 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9464
9465 if ( !node->args() )
9466 return QSet<QString>();
9467
9468 QSet<QString> referencedCols;
9469 QSet<QString> referencedVars;
9470
9471 if ( node->args()->count() > 2 )
9472 {
9473 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9474 referencedVars = subExpressionNode->referencedVariables();
9475 referencedCols = subExpressionNode->referencedColumns();
9476 }
9477 if ( node->args()->count() > 3 )
9478 {
9479 QgsExpressionNode *filterNode = node->args()->at( 3 );
9480 referencedVars = filterNode->referencedVariables();
9481 referencedCols.unite( filterNode->referencedColumns() );
9482 }
9483
9484 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
9485 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9486 else
9487 return referencedCols;
9488 },
9489 true
9490 )
9491
9492 << new QgsStaticExpressionFunction(
9493 u"relation_aggregate"_s,
9495 << QgsExpressionFunction::Parameter( u"relation"_s )
9496 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9497 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9498 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9499 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9500 fcnAggregateRelation,
9501 u"Aggregates"_s,
9502 QString(),
9503 false,
9504 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9505 true
9506 )
9507
9508 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9509 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9510 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9511 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9512 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9513 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9514 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9515 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9516 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9517 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9518 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9519 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9520 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9521 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9522 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9523 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9524 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9525 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9526 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9527 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9528 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9529
9530 << 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 )
9531 << new QgsStaticExpressionFunction(
9532 u"regexp_matches"_s,
9534 << QgsExpressionFunction::Parameter( u"string"_s )
9535 << QgsExpressionFunction::Parameter( u"regex"_s )
9536 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
9537 fcnRegexpMatches,
9538 u"Arrays"_s
9539 )
9540
9541 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9542 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s ) << QgsExpressionFunction::Parameter( u"datetime2"_s ), fcnAge, u"Date and Time"_s )
9543 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9544 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9545 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9546 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9547 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9548 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9549 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9550 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9551 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9552 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9553 << 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 )
9554 << new QgsStaticExpressionFunction(
9555 u"make_time"_s,
9556 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s ) << QgsExpressionFunction::Parameter( u"minute"_s ) << QgsExpressionFunction::Parameter( u"second"_s ),
9557 fcnMakeTime,
9558 u"Date and Time"_s
9559 )
9560 << new QgsStaticExpressionFunction(
9561 u"make_datetime"_s,
9563 << QgsExpressionFunction::Parameter( u"year"_s )
9564 << QgsExpressionFunction::Parameter( u"month"_s )
9565 << QgsExpressionFunction::Parameter( u"day"_s )
9566 << QgsExpressionFunction::Parameter( u"hour"_s )
9567 << QgsExpressionFunction::Parameter( u"minute"_s )
9568 << QgsExpressionFunction::Parameter( u"second"_s ),
9569 fcnMakeDateTime,
9570 u"Date and Time"_s
9571 )
9572 << new QgsStaticExpressionFunction(
9573 u"make_interval"_s,
9575 << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9576 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9577 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9578 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9579 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9580 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9581 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9582 fcnMakeInterval,
9583 u"Date and Time"_s
9584 )
9585 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9586 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9587 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9588 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9589 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9590 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9591 << new QgsStaticExpressionFunction(
9592 u"substr_count"_s,
9594 << QgsExpressionFunction::Parameter( u"string"_s )
9595 << QgsExpressionFunction::Parameter( u"substring"_s )
9596 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9597 fcnSubstrCount,
9598 u"String"_s
9599 )
9600 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9601 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9602 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9603 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9604 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9605 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9606 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9607 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9608 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9609 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9610 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9611 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9612 << new QgsStaticExpressionFunction(
9613 u"wordwrap"_s,
9615 << QgsExpressionFunction::Parameter( u"text"_s )
9616 << QgsExpressionFunction::Parameter( u"length"_s )
9617 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ),
9618 fcnWordwrap,
9619 u"String"_s
9620 )
9621 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9622 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9623 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s ) }, fcnRepeat, u"String"_s )
9624 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9625 << new QgsStaticExpressionFunction(
9626 u"regexp_replace"_s,
9627 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"replacement"_s ),
9628 fcnRegexpReplace,
9629 u"String"_s
9630 )
9631 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9632 << new QgsStaticExpressionFunction(
9633 u"substr"_s,
9634 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ),
9635 fcnSubstr,
9636 u"String"_s,
9637 QString(),
9638 false,
9639 QSet< QString >(),
9640 false,
9641 QStringList(),
9642 true
9643 )
9644 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9645 << new QgsStaticExpressionFunction( u"concat_ws"_s, -1, fcnConcatWs, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9646 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9647 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9648 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9649 << new QgsStaticExpressionFunction(
9650 u"rpad"_s,
9651 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s, true, u" "_s ),
9652 fcnRPad,
9653 u"String"_s
9654 )
9655 << new QgsStaticExpressionFunction(
9656 u"lpad"_s,
9657 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s, true, u" "_s ),
9658 fcnLPad,
9659 u"String"_s
9660 )
9661 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9662 << new QgsStaticExpressionFunction(
9663 u"format_number"_s,
9665 << QgsExpressionFunction::Parameter( u"number"_s )
9666 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9667 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9668 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9669 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ),
9670 fcnFormatNumber,
9671 u"String"_s
9672 )
9673 << new QgsStaticExpressionFunction(
9674 u"format_date"_s,
9676 << QgsExpressionFunction::Parameter( u"datetime"_s )
9677 << QgsExpressionFunction::Parameter( u"format"_s )
9678 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9679 fcnFormatDate,
9680 QStringList() << u"String"_s << u"Date and Time"_s
9681 )
9682 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9683 << new QgsStaticExpressionFunction(
9684 u"color_mix_rgb"_s,
9685 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9686 fcnColorMixRgb,
9687 u"Color"_s
9688 )
9689 << new QgsStaticExpressionFunction(
9690 u"color_mix"_s,
9691 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9692 fcnColorMix,
9693 u"Color"_s
9694 )
9695 << 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 )
9696 << new QgsStaticExpressionFunction(
9697 u"color_rgbf"_s,
9699 << QgsExpressionFunction::Parameter( u"red"_s )
9700 << QgsExpressionFunction::Parameter( u"green"_s )
9701 << QgsExpressionFunction::Parameter( u"blue"_s )
9702 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9703 fcnColorRgbF,
9704 u"Color"_s
9705 )
9706 << new QgsStaticExpressionFunction(
9707 u"color_rgba"_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 ),
9713 fncColorRgba,
9714 u"Color"_s
9715 )
9716 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColor, u"Color"_s )
9717 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColorObject, u"Color"_s )
9718 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ), fcnCreateRamp, u"Color"_s )
9719 << new QgsStaticExpressionFunction(
9720 u"color_hsl"_s,
9721 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"lightness"_s ),
9722 fcnColorHsl,
9723 u"Color"_s
9724 )
9725 << new QgsStaticExpressionFunction(
9726 u"color_hsla"_s,
9728 << QgsExpressionFunction::Parameter( u"hue"_s )
9729 << QgsExpressionFunction::Parameter( u"saturation"_s )
9730 << QgsExpressionFunction::Parameter( u"lightness"_s )
9731 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9732 fncColorHsla,
9733 u"Color"_s
9734 )
9735 << new QgsStaticExpressionFunction(
9736 u"color_hslf"_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, true, 1. ),
9742 fcnColorHslF,
9743 u"Color"_s
9744 )
9745 << new QgsStaticExpressionFunction(
9746 u"color_hsv"_s,
9747 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9748 fcnColorHsv,
9749 u"Color"_s
9750 )
9751 << new QgsStaticExpressionFunction(
9752 u"color_hsva"_s,
9754 << QgsExpressionFunction::Parameter( u"hue"_s )
9755 << QgsExpressionFunction::Parameter( u"saturation"_s )
9756 << QgsExpressionFunction::Parameter( u"value"_s )
9757 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9758 fncColorHsva,
9759 u"Color"_s
9760 )
9761 << new QgsStaticExpressionFunction(
9762 u"color_hsvf"_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, true, 1. ),
9768 fcnColorHsvF,
9769 u"Color"_s
9770 )
9771 << new QgsStaticExpressionFunction(
9772 u"color_cmyk"_s,
9774 << QgsExpressionFunction::Parameter( u"cyan"_s )
9775 << QgsExpressionFunction::Parameter( u"magenta"_s )
9776 << QgsExpressionFunction::Parameter( u"yellow"_s )
9777 << QgsExpressionFunction::Parameter( u"black"_s ),
9778 fcnColorCmyk,
9779 u"Color"_s
9780 )
9781 << new QgsStaticExpressionFunction(
9782 u"color_cmyka"_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 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9789 fncColorCmyka,
9790 u"Color"_s
9791 )
9792 << new QgsStaticExpressionFunction(
9793 u"color_cmykf"_s,
9795 << QgsExpressionFunction::Parameter( u"cyan"_s )
9796 << QgsExpressionFunction::Parameter( u"magenta"_s )
9797 << QgsExpressionFunction::Parameter( u"yellow"_s )
9798 << QgsExpressionFunction::Parameter( u"black"_s )
9799 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9800 fcnColorCmykF,
9801 u"Color"_s
9802 )
9803 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ), fncColorPart, u"Color"_s )
9804 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncDarker, u"Color"_s )
9805 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncLighter, u"Color"_s )
9806 << new QgsStaticExpressionFunction(
9807 u"set_color_part"_s,
9808 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9809 fncSetColorPart,
9810 u"Color"_s
9811 )
9812
9813 // file info
9814 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnBaseFileName, u"Files and Paths"_s )
9815 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSuffix, u"Files and Paths"_s )
9816 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileExists, u"Files and Paths"_s )
9817 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileName, u"Files and Paths"_s )
9818 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsFile, u"Files and Paths"_s )
9819 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsDir, u"Files and Paths"_s )
9820 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFilePath, u"Files and Paths"_s )
9821 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSize, u"Files and Paths"_s )
9822
9823 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ), fcnExif, u"Files and Paths"_s )
9824 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnExifGeoTag, u"GeometryGroup"_s )
9825
9826 // hash
9827 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ), fcnGenericHash, u"Conversions"_s )
9828 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashMd5, u"Conversions"_s )
9829 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashSha256, u"Conversions"_s )
9830
9831 //base64
9832 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBase64, u"Conversions"_s )
9833 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnFromBase64, u"Conversions"_s )
9834
9835 // magnetic models
9836 << new QgsStaticExpressionFunction(
9837 u"magnetic_declination"_s,
9839 << QgsExpressionFunction::Parameter( u"model_name"_s )
9840 << QgsExpressionFunction::Parameter( u"date"_s )
9841 << QgsExpressionFunction::Parameter( u"latitude"_s )
9842 << QgsExpressionFunction::Parameter( u"longitude"_s )
9843 << QgsExpressionFunction::Parameter( u"height"_s )
9844 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9845 fcnMagneticDeclination,
9846 u"MagneticModels"_s
9847 )
9848 << new QgsStaticExpressionFunction(
9849 u"magnetic_inclination"_s,
9851 << QgsExpressionFunction::Parameter( u"model_name"_s )
9852 << QgsExpressionFunction::Parameter( u"date"_s )
9853 << QgsExpressionFunction::Parameter( u"latitude"_s )
9854 << QgsExpressionFunction::Parameter( u"longitude"_s )
9855 << QgsExpressionFunction::Parameter( u"height"_s )
9856 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9857 fcnMagneticInclination,
9858 u"MagneticModels"_s
9859 )
9860 << new QgsStaticExpressionFunction(
9861 u"magnetic_declination_rate_of_change"_s,
9863 << QgsExpressionFunction::Parameter( u"model_name"_s )
9864 << QgsExpressionFunction::Parameter( u"date"_s )
9865 << QgsExpressionFunction::Parameter( u"latitude"_s )
9866 << QgsExpressionFunction::Parameter( u"longitude"_s )
9867 << QgsExpressionFunction::Parameter( u"height"_s )
9868 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9869 fcnMagneticDeclinationRateOfChange,
9870 u"MagneticModels"_s
9871 )
9872 << new QgsStaticExpressionFunction(
9873 u"magnetic_inclination_rate_of_change"_s,
9875 << QgsExpressionFunction::Parameter( u"model_name"_s )
9876 << QgsExpressionFunction::Parameter( u"date"_s )
9877 << QgsExpressionFunction::Parameter( u"latitude"_s )
9878 << QgsExpressionFunction::Parameter( u"longitude"_s )
9879 << QgsExpressionFunction::Parameter( u"height"_s )
9880 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9881 fcnMagneticInclinationRateOfChange,
9882 u"MagneticModels"_s
9883 )
9884
9885 // deprecated stuff - hidden from users
9886 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9887
9888 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9889 geomFunc->setIsStatic( false );
9890 functions << geomFunc;
9891
9892 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9893 areaFunc->setIsStatic( false );
9894 functions << areaFunc;
9895
9896 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9897
9898 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9899 lengthFunc->setIsStatic( false );
9900 functions << lengthFunc;
9901
9902 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9903 perimeterFunc->setIsStatic( false );
9904 functions << perimeterFunc;
9905
9906 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9907
9908 functions << new QgsStaticExpressionFunction( u"roundness"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnRoundness, u"GeometryGroup"_s );
9909
9910 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9911 xFunc->setIsStatic( false );
9912 functions << xFunc;
9913
9914 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9915 yFunc->setIsStatic( false );
9916 functions << yFunc;
9917
9918 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9919 zFunc->setIsStatic( false );
9920 functions << zFunc;
9921
9922 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions {
9923 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9924 { u"overlay_contains"_s, fcnGeomOverlayContains },
9925 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9926 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9927 { u"overlay_equals_exact"_s, fcnGeomOverlayEqualsExact },
9928 { u"overlay_equals_topological"_s, fcnGeomOverlayEqualsTopological },
9929 { u"overlay_equals_fuzzy"_s, fcnGeomOverlayEqualsFuzzy },
9930 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9931 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9932 { u"overlay_within"_s, fcnGeomOverlayWithin },
9933 };
9934 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9935 while ( i.hasNext() )
9936 {
9937 i.next();
9938 QString defaultBackend = i.key() == "overlay_equals"_L1 ? QString( "QGIS" ) : QString( "GEOS" );
9939 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction(
9940 i.key(),
9942 << QgsExpressionFunction::Parameter( u"layer"_s )
9943 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9944 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9945 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9946 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9947 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9948 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9949 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9950 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false )
9951 << QgsExpressionFunction::Parameter( u"backend"_s, true, defaultBackend, false )
9952 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4, false ),
9953 i.value(),
9954 u"GeometryGroup"_s,
9955 QString(),
9956 true,
9957 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9958 true
9959 );
9960
9961 // The current feature is accessed for the geometry, so this should not be cached
9962 fcnGeomOverlayFunc->setIsStatic( false );
9963 functions << fcnGeomOverlayFunc;
9964 }
9965
9966 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction(
9967 u"overlay_nearest"_s,
9969 << QgsExpressionFunction::Parameter( u"layer"_s )
9970 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9971 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9972 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9973 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9974 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9975 fcnGeomOverlayNearest,
9976 u"GeometryGroup"_s,
9977 QString(),
9978 true,
9979 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9980 true
9981 );
9982 // The current feature is accessed for the geometry, so this should not be cached
9983 fcnGeomOverlayNearestFunc->setIsStatic( false );
9984 functions << fcnGeomOverlayNearestFunc;
9985
9986 functions
9987 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9988 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9989 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
9990 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
9991 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
9992 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
9993 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
9994 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
9995 << 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 )
9996 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
9997 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
9998 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
9999 << 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 )
10000 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
10001 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
10002 << new QgsStaticExpressionFunction(
10003 u"make_triangle"_s,
10004 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ) << QgsExpressionFunction::Parameter( u"point3"_s ),
10005 fcnMakeTriangle,
10006 u"GeometryGroup"_s
10007 )
10008 << new QgsStaticExpressionFunction(
10009 u"make_circle"_s,
10011 << QgsExpressionFunction::Parameter( u"center"_s )
10012 << QgsExpressionFunction::Parameter( u"radius"_s )
10013 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
10014 fcnMakeCircle,
10015 u"GeometryGroup"_s
10016 )
10017 << new QgsStaticExpressionFunction(
10018 u"make_ellipse"_s,
10020 << QgsExpressionFunction::Parameter( u"center"_s )
10021 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
10022 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
10023 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10024 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
10025 fcnMakeEllipse,
10026 u"GeometryGroup"_s
10027 )
10028 << new QgsStaticExpressionFunction(
10029 u"make_regular_polygon"_s,
10031 << QgsExpressionFunction::Parameter( u"center"_s )
10032 << QgsExpressionFunction::Parameter( u"radius"_s )
10033 << QgsExpressionFunction::Parameter( u"number_sides"_s )
10034 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
10035 fcnMakeRegularPolygon,
10036 u"GeometryGroup"_s
10037 )
10038 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnMakeSquare, u"GeometryGroup"_s )
10039 << new QgsStaticExpressionFunction(
10040 u"make_rectangle_3points"_s,
10042 << QgsExpressionFunction::Parameter( u"point1"_s )
10043 << QgsExpressionFunction::Parameter( u"point2"_s )
10044 << QgsExpressionFunction::Parameter( u"point3"_s )
10045 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
10046 fcnMakeRectangleFrom3Points,
10047 u"GeometryGroup"_s
10048 )
10049 << new QgsStaticExpressionFunction(
10050 u"make_valid"_s,
10052 QgsExpressionFunction::Parameter( u"geometry"_s ),
10053#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
10054 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
10055#else
10056 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
10057#endif
10058 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
10059 },
10060 fcnGeomMakeValid,
10061 u"GeometryGroup"_s
10062 );
10063
10064 functions
10065 << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
10066 functions
10067 << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
10068 functions
10069 << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
10070 functions
10071 << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
10072
10073 QgsStaticExpressionFunction *xAtFunc
10074 = 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 );
10075 xAtFunc->setIsStatic( false );
10076 functions << xAtFunc;
10077
10078
10079 QgsStaticExpressionFunction *yAtFunc
10080 = 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 );
10081 yAtFunc->setIsStatic( false );
10082 functions << yAtFunc;
10083
10084 functions
10085 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
10086 << 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 )
10087 << 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 )
10088 << 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 )
10089 << 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 )
10090 << 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 )
10091 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10092 << 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 )
10093 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
10094 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
10095 << new QgsStaticExpressionFunction(
10096 u"intersects_bbox"_s,
10097 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10098 fcnBbox,
10099 u"GeometryGroup"_s,
10100 QString(),
10101 false,
10102 QSet<QString>(),
10103 false,
10104 QStringList() << u"bbox"_s
10105 )
10106 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDisjoint, u"GeometryGroup"_s )
10107 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersects, u"GeometryGroup"_s )
10108 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnTouches, u"GeometryGroup"_s )
10109 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCrosses, u"GeometryGroup"_s )
10110 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnContains, u"GeometryGroup"_s )
10111 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnOverlaps, u"GeometryGroup"_s )
10112 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnWithin, u"GeometryGroup"_s )
10113 << new QgsStaticExpressionFunction( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnEquals, u"GeometryGroup"_s )
10114 << new QgsStaticExpressionFunction(
10115 u"equals_exact"_s,
10117 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10118 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10119 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s ),
10120 fcnIsEqualsExact,
10121 u"GeometryGroup"_s
10122 )
10123 << new QgsStaticExpressionFunction(
10124 u"equals_topological"_s,
10126 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10127 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10128 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"GEOS"_s ),
10129 fcnIsEqualsTopological,
10130 u"GeometryGroup"_s
10131 )
10132 << new QgsStaticExpressionFunction(
10133 u"equals_fuzzy"_s,
10135 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10136 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10137 << QgsExpressionFunction::Parameter( u"backend"_s, true, u"QGIS"_s )
10138 << QgsExpressionFunction::Parameter( u"epsilon"_s, true, 1e-4 ),
10139 fcnIsEqualsFuzzy,
10140 u"GeometryGroup"_s
10141 )
10142 << 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 )
10143 << new QgsStaticExpressionFunction(
10144 u"rotate"_s,
10146 << QgsExpressionFunction::Parameter( u"geometry"_s )
10147 << QgsExpressionFunction::Parameter( u"rotation"_s )
10148 << QgsExpressionFunction::Parameter( u"center"_s, true )
10149 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
10150 fcnRotate,
10151 u"GeometryGroup"_s
10152 )
10153 << new QgsStaticExpressionFunction(
10154 u"scale"_s,
10156 << QgsExpressionFunction::Parameter( u"geometry"_s )
10157 << QgsExpressionFunction::Parameter( u"x_scale"_s )
10158 << QgsExpressionFunction::Parameter( u"y_scale"_s )
10159 << QgsExpressionFunction::Parameter( u"center"_s, true ),
10160 fcnScale,
10161 u"GeometryGroup"_s
10162 )
10163 << new QgsStaticExpressionFunction(
10164 u"affine_transform"_s,
10166 << QgsExpressionFunction::Parameter( u"geometry"_s )
10167 << QgsExpressionFunction::Parameter( u"delta_x"_s )
10168 << QgsExpressionFunction::Parameter( u"delta_y"_s )
10169 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
10170 << QgsExpressionFunction::Parameter( u"scale_x"_s )
10171 << QgsExpressionFunction::Parameter( u"scale_y"_s )
10172 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
10173 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
10174 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
10175 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
10176 fcnAffineTransform,
10177 u"GeometryGroup"_s
10178 )
10179 << new QgsStaticExpressionFunction(
10180 u"buffer"_s,
10182 << QgsExpressionFunction::Parameter( u"geometry"_s )
10183 << QgsExpressionFunction::Parameter( u"distance"_s )
10184 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
10185 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
10186 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
10187 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
10188 fcnBuffer,
10189 u"GeometryGroup"_s
10190 )
10191 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForceRHR, u"GeometryGroup"_s )
10192 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCW, u"GeometryGroup"_s )
10193 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCCW, u"GeometryGroup"_s )
10194 << new QgsStaticExpressionFunction(
10195 u"wedge_buffer"_s,
10197 << QgsExpressionFunction::Parameter( u"center"_s )
10198 << QgsExpressionFunction::Parameter( u"azimuth"_s )
10199 << QgsExpressionFunction::Parameter( u"width"_s )
10200 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
10201 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ),
10202 fcnWedgeBuffer,
10203 u"GeometryGroup"_s
10204 )
10205 << new QgsStaticExpressionFunction(
10206 u"tapered_buffer"_s,
10208 << QgsExpressionFunction::Parameter( u"geometry"_s )
10209 << QgsExpressionFunction::Parameter( u"start_width"_s )
10210 << QgsExpressionFunction::Parameter( u"end_width"_s )
10211 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ),
10212 fcnTaperedBuffer,
10213 u"GeometryGroup"_s
10214 )
10215 << 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 )
10216 << new QgsStaticExpressionFunction(
10217 u"offset_curve"_s,
10219 << QgsExpressionFunction::Parameter( u"geometry"_s )
10220 << QgsExpressionFunction::Parameter( u"distance"_s )
10221 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10222 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10223 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10224 fcnOffsetCurve,
10225 u"GeometryGroup"_s
10226 )
10227 << new QgsStaticExpressionFunction(
10228 u"single_sided_buffer"_s,
10230 << QgsExpressionFunction::Parameter( u"geometry"_s )
10231 << QgsExpressionFunction::Parameter( u"distance"_s )
10232 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
10233 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
10234 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
10235 fcnSingleSidedBuffer,
10236 u"GeometryGroup"_s
10237 )
10238 << new QgsStaticExpressionFunction(
10239 u"extend"_s,
10241 << QgsExpressionFunction::Parameter( u"geometry"_s )
10242 << QgsExpressionFunction::Parameter( u"start_distance"_s )
10243 << QgsExpressionFunction::Parameter( u"end_distance"_s )
10244 << QgsExpressionFunction::Parameter( u"start_deflection"_s, true, 0 )
10245 << QgsExpressionFunction::Parameter( u"end_deflection"_s, true, 0 ),
10246 fcnExtend,
10247 u"GeometryGroup"_s
10248 )
10249 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
10250 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
10251 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
10252 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
10253 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
10254 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnInteriorRingN, u"GeometryGroup"_s )
10255 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnGeometryN, u"GeometryGroup"_s )
10256 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
10257 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
10258 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry1"_s ), QgsExpressionFunction::Parameter( u"geometry2"_s ) }, fcnSharedPaths, u"GeometryGroup"_s )
10259 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
10260 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
10261 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
10262 << new QgsStaticExpressionFunction(
10263 u"smooth"_s,
10265 << QgsExpressionFunction::Parameter( u"geometry"_s )
10266 << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
10267 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
10268 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
10269 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ),
10270 fcnSmooth,
10271 u"GeometryGroup"_s
10272 )
10273 << new QgsStaticExpressionFunction(
10274 u"triangular_wave"_s,
10275 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10276 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10277 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10278 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10279 fcnTriangularWave,
10280 u"GeometryGroup"_s
10281 )
10282 << new QgsStaticExpressionFunction(
10283 u"triangular_wave_randomized"_s,
10284 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10285 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10286 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10287 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10288 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10289 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10290 fcnTriangularWaveRandomized,
10291 u"GeometryGroup"_s
10292 )
10293 << new QgsStaticExpressionFunction(
10294 u"square_wave"_s,
10295 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10296 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10297 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10298 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10299 fcnSquareWave,
10300 u"GeometryGroup"_s
10301 )
10302 << new QgsStaticExpressionFunction(
10303 u"square_wave_randomized"_s,
10304 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10305 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10306 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10307 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10308 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10309 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10310 fcnSquareWaveRandomized,
10311 u"GeometryGroup"_s
10312 )
10313 << new QgsStaticExpressionFunction(
10314 u"wave"_s,
10315 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10316 QgsExpressionFunction::Parameter( u"wavelength"_s ),
10317 QgsExpressionFunction::Parameter( u"amplitude"_s ),
10318 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
10319 fcnRoundWave,
10320 u"GeometryGroup"_s
10321 )
10322 << new QgsStaticExpressionFunction(
10323 u"wave_randomized"_s,
10324 { QgsExpressionFunction::Parameter( u"geometry"_s ),
10325 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
10326 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
10327 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
10328 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
10329 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
10330 fcnRoundWaveRandomized,
10331 u"GeometryGroup"_s
10332 )
10333 << new QgsStaticExpressionFunction(
10334 u"apply_dash_pattern"_s,
10335 {
10336 QgsExpressionFunction::Parameter( u"geometry"_s ),
10337 QgsExpressionFunction::Parameter( u"pattern"_s ),
10338 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
10339 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
10340 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
10341 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
10342 },
10343 fcnApplyDashPattern,
10344 u"GeometryGroup"_s
10345 )
10346 << new QgsStaticExpressionFunction( u"densify_by_count"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"vertices"_s ) }, fcnDensifyByCount, u"GeometryGroup"_s )
10347 << new QgsStaticExpressionFunction( u"densify_by_distance"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"distance"_s ) }, fcnDensifyByDistance, u"GeometryGroup"_s )
10348 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
10349 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
10350 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
10351 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
10352 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
10353 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
10354 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
10355 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
10356 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
10357 << new QgsStaticExpressionFunction(
10358 u"is_empty_or_null"_s,
10359 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
10360 fcnIsEmptyOrNull,
10361 u"GeometryGroup"_s,
10362 QString(),
10363 false,
10364 QSet<QString>(),
10365 false,
10366 QStringList(),
10367 true
10368 )
10369 << 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 )
10370#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
10371 << new QgsStaticExpressionFunction(
10372 u"concave_hull"_s,
10374 << QgsExpressionFunction::Parameter( u"geometry"_s )
10375 << QgsExpressionFunction::Parameter( u"target_percent"_s )
10376 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ),
10377 fcnConcaveHull,
10378 u"GeometryGroup"_s
10379 )
10380#endif
10381 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnOrientedBBox, u"GeometryGroup"_s )
10382 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMainAngle, u"GeometryGroup"_s )
10383 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ), fcnMinimalCircle, u"GeometryGroup"_s )
10384 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDifference, u"GeometryGroup"_s )
10385 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDistance, u"GeometryGroup"_s )
10386 << new QgsStaticExpressionFunction(
10387 u"hausdorff_distance"_s,
10389 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10390 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10391 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
10392 fcnHausdorffDistance,
10393 u"GeometryGroup"_s
10394 )
10395 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersection, u"GeometryGroup"_s )
10396 << new QgsStaticExpressionFunction(
10397 u"sym_difference"_s,
10398 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10399 fcnSymDifference,
10400 u"GeometryGroup"_s,
10401 QString(),
10402 false,
10403 QSet<QString>(),
10404 false,
10405 QStringList() << u"symDifference"_s
10406 )
10407 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10408 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10409 << new QgsStaticExpressionFunction(
10410 u"geom_to_wkt"_s,
10411 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
10412 fcnGeomToWKT,
10413 u"GeometryGroup"_s,
10414 QString(),
10415 false,
10416 QSet<QString>(),
10417 false,
10418 QStringList() << u"geomToWKT"_s
10419 )
10420 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10421 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
10422 << new QgsStaticExpressionFunction(
10423 u"transform"_s,
10424 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"source_auth_id"_s ) << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
10425 fcnTransformGeometry,
10426 u"GeometryGroup"_s
10427 )
10428 << new QgsStaticExpressionFunction(
10429 u"extrude"_s,
10430 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ),
10431 fcnExtrude,
10432 u"GeometryGroup"_s,
10433 QString()
10434 )
10435 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsMultipart, u"GeometryGroup"_s )
10436 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMax, u"GeometryGroup"_s )
10437 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMin, u"GeometryGroup"_s )
10438 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMax, u"GeometryGroup"_s )
10439 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMin, u"GeometryGroup"_s )
10440 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSinuosity, u"GeometryGroup"_s )
10441 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStraightDistance2d, u"GeometryGroup"_s );
10442
10443
10444 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction(
10445 u"order_parts"_s,
10447 << QgsExpressionFunction::Parameter( u"geometry"_s )
10448 << QgsExpressionFunction::Parameter( u"orderby"_s )
10449 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
10450 fcnOrderParts,
10451 u"GeometryGroup"_s,
10452 QString()
10453 );
10454
10455 orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10456 const QList< QgsExpressionNode *> argList = node->args()->list();
10457 for ( QgsExpressionNode *argNode : argList )
10458 {
10459 if ( !argNode->isStatic( parent, context ) )
10460 return false;
10461 }
10462
10463 if ( node->args()->count() > 1 )
10464 {
10465 QgsExpressionNode *argNode = node->args()->at( 1 );
10466
10467 QString expString = argNode->eval( parent, context ).toString();
10468
10469 QgsExpression e( expString );
10470
10471 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10472 return true;
10473 }
10474
10475 return true;
10476 } );
10477
10478 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10479 if ( node->args()->count() > 1 )
10480 {
10481 QgsExpressionNode *argNode = node->args()->at( 1 );
10482 QString expression = argNode->eval( parent, context ).toString();
10484 e.prepare( context );
10485 context->setCachedValue( expression, QVariant::fromValue( e ) );
10486 }
10487 return true;
10488 } );
10489 functions << orderPartsFunc;
10490
10491 functions
10492 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnClosestPoint, u"GeometryGroup"_s )
10493 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnShortestLine, u"GeometryGroup"_s )
10494 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
10495 << new QgsStaticExpressionFunction(
10496 u"line_interpolate_point_by_m"_s,
10498 << QgsExpressionFunction::Parameter( u"geometry"_s )
10499 << QgsExpressionFunction::Parameter( u"m"_s )
10500 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10501 fcnLineInterpolatePointByM,
10502 u"GeometryGroup"_s
10503 )
10504 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
10505 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
10506 << new QgsStaticExpressionFunction(
10507 u"line_locate_m"_s,
10509 << QgsExpressionFunction::Parameter( u"geometry"_s )
10510 << QgsExpressionFunction::Parameter( u"m"_s )
10511 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10512 fcnLineLocateM,
10513 u"GeometryGroup"_s
10514 )
10515 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
10516 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
10517 << new QgsStaticExpressionFunction(
10518 u"line_substring"_s,
10519 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10520 fcnLineSubset,
10521 u"GeometryGroup"_s
10522 );
10523
10524
10525 // **Record** functions
10526
10527 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
10528 idFunc->setIsStatic( false );
10529 functions << idFunc;
10530
10531 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
10532 currentFeatureFunc->setIsStatic( false );
10533 functions << currentFeatureFunc;
10534
10535 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction(
10536 u"uuid"_s,
10537 { QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ), QgsExpressionFunction::Parameter( u"version"_s, true, 4 ) },
10538 fcnUuid,
10539 u"Record and Attributes"_s,
10540 QString(),
10541 false,
10542 QSet<QString>(),
10543 false,
10544 QStringList() << u"$uuid"_s
10545 );
10546 uuidFunc->setIsStatic( false );
10547 functions << uuidFunc;
10548
10549 functions
10550 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
10551 << new QgsStaticExpressionFunction(
10552 u"get_feature"_s,
10553 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"value"_s, true ),
10554 fcnGetFeature,
10555 u"Record and Attributes"_s,
10556 QString(),
10557 false,
10558 QSet<QString>(),
10559 false,
10560 QStringList() << u"QgsExpressionUtils::getFeature"_s
10561 )
10562 << new QgsStaticExpressionFunction(
10563 u"get_feature_by_id"_s,
10564 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"feature_id"_s ),
10565 fcnGetFeatureById,
10566 u"Record and Attributes"_s,
10567 QString(),
10568 false,
10569 QSet<QString>(),
10570 false
10571 );
10572
10573 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction(
10574 u"attributes"_s,
10575 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
10576 fcnAttributes,
10577 u"Record and Attributes"_s,
10578 QString(),
10579 false,
10580 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10581 );
10582 attributesFunc->setIsStatic( false );
10583 functions << attributesFunc;
10584 QgsStaticExpressionFunction *representAttributesFunc
10585 = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1, fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10586 representAttributesFunc->setIsStatic( false );
10587 functions << representAttributesFunc;
10588
10589 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction(
10590 u"is_feature_valid"_s,
10592 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10593 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10594 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10595 fcnValidateFeature,
10596 u"Record and Attributes"_s,
10597 QString(),
10598 false,
10599 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10600 );
10601 validateFeature->setIsStatic( false );
10602 functions << validateFeature;
10603
10604 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction(
10605 u"is_attribute_valid"_s,
10607 << QgsExpressionFunction::Parameter( u"attribute"_s, false )
10609 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10610 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10611 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10612 fcnValidateAttribute,
10613 u"Record and Attributes"_s,
10614 QString(),
10615 false,
10616 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10617 );
10618 validateAttribute->setIsStatic( false );
10619 functions << validateAttribute;
10620
10621 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction( u"maptip"_s, -1, fcnFeatureMaptip, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10622 maptipFunc->setIsStatic( false );
10623 functions << maptipFunc;
10624
10625 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction( u"display_expression"_s, -1, fcnFeatureDisplayExpression, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10626 displayFunc->setIsStatic( false );
10627 functions << displayFunc;
10628
10629 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( u"is_selected"_s, -1, fcnIsSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10630 isSelectedFunc->setIsStatic( false );
10631 functions << isSelectedFunc;
10632
10633 functions << new QgsStaticExpressionFunction( u"num_selected"_s, -1, fcnNumSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10634
10635 functions << new QgsStaticExpressionFunction(
10636 u"sqlite_fetch_and_increment"_s,
10638 << QgsExpressionFunction::Parameter( u"database"_s )
10639 << QgsExpressionFunction::Parameter( u"table"_s )
10640 << QgsExpressionFunction::Parameter( u"id_field"_s )
10641 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
10642 << QgsExpressionFunction::Parameter( u"filter_value"_s )
10643 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
10644 fcnSqliteFetchAndIncrement,
10645 u"Record and Attributes"_s
10646 );
10647
10648 // **CRS** functions
10649 functions
10650 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
10651 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
10652
10653
10654 // **Fields and Values** functions
10655 QgsStaticExpressionFunction *representValueFunc
10656 = 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 );
10657
10658 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10659 Q_UNUSED( context )
10660 if ( node->args()->count() == 1 )
10661 {
10662 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
10663 if ( colRef )
10664 {
10665 return true;
10666 }
10667 else
10668 {
10669 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
10670 return false;
10671 }
10672 }
10673 else if ( node->args()->count() == 2 )
10674 {
10675 return true;
10676 }
10677 else
10678 {
10679 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
10680 return false;
10681 }
10682 } );
10683
10684 functions << representValueFunc;
10685
10686 // **General** functions
10687 functions
10688 << new QgsStaticExpressionFunction(
10689 u"layer_property"_s,
10691 << QgsExpressionFunction::Parameter( u"layer"_s )
10692 << QgsExpressionFunction::Parameter( u"property"_s )
10693 << QgsExpressionFunction::Parameter( u"translate"_s, true, true ),
10694 fcnGetLayerProperty,
10695 u"Map Layers"_s
10696 )
10697 << new QgsStaticExpressionFunction( u"decode_uri"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"part"_s, true ), fcnDecodeUri, u"Map Layers"_s )
10698 << new QgsStaticExpressionFunction( u"mime_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary_data"_s ), fcnMimeType, u"General"_s )
10699 << new QgsStaticExpressionFunction(
10700 u"raster_statistic"_s,
10701 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"statistic"_s ),
10702 fcnGetRasterBandStat,
10703 u"Rasters"_s
10704 );
10705
10706 // **var** function
10707 QgsStaticExpressionFunction *varFunction
10708 = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
10709 varFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10710 /* A variable node is static if it has a static name and the name can be found at prepare
10711 * time and is tagged with isStatic.
10712 * It is not static if a variable is set during iteration or not tagged isStatic.
10713 * (e.g. geom_part variable)
10714 */
10715 if ( node->args()->count() > 0 )
10716 {
10717 QgsExpressionNode *argNode = node->args()->at( 0 );
10718
10719 if ( !argNode->isStatic( parent, context ) )
10720 return false;
10721
10722 const QString varName = argNode->eval( parent, context ).toString();
10723 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
10724 return false;
10725
10726 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
10727 return scope ? scope->isStatic( varName ) : false;
10728 }
10729 return false;
10730 } );
10731 varFunction->setUsesGeometryFunction( []( const QgsExpressionNodeFunction *node ) -> bool {
10732 if ( node && node->args()->count() > 0 )
10733 {
10734 QgsExpressionNode *argNode = node->args()->at( 0 );
10735 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
10736 {
10737 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
10738 return true;
10739 }
10740 }
10741 return false;
10742 } );
10743
10744 functions << varFunction;
10745
10746 QgsStaticExpressionFunction *evalTemplateFunction
10747 = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10748 evalTemplateFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10749 if ( node->args()->count() > 0 )
10750 {
10751 QgsExpressionNode *argNode = node->args()->at( 0 );
10752
10753 if ( argNode->isStatic( parent, context ) )
10754 {
10755 QString expString = argNode->eval( parent, context ).toString();
10756
10757 QgsExpression e( expString );
10758
10759 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10760 return true;
10761 }
10762 }
10763
10764 return false;
10765 } );
10766 functions << evalTemplateFunction;
10767
10768 QgsStaticExpressionFunction *evalFunc
10769 = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10770 evalFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10771 if ( node->args()->count() > 0 )
10772 {
10773 QgsExpressionNode *argNode = node->args()->at( 0 );
10774
10775 if ( argNode->isStatic( parent, context ) )
10776 {
10777 QString expString = argNode->eval( parent, context ).toString();
10778
10779 QgsExpression e( expString );
10780
10781 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10782 return true;
10783 }
10784 }
10785
10786 return false;
10787 } );
10788
10789 functions << evalFunc;
10790
10791 QgsStaticExpressionFunction *attributeFunc
10792 = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10793 attributeFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10794 const QList< QgsExpressionNode *> argList = node->args()->list();
10795 for ( QgsExpressionNode *argNode : argList )
10796 {
10797 if ( !argNode->isStatic( parent, context ) )
10798 return false;
10799 }
10800
10801 if ( node->args()->count() == 1 )
10802 {
10803 // not static -- this is the variant which uses the current feature taken direct from the expression context
10804 return false;
10805 }
10806
10807 return true;
10808 } );
10809 functions << attributeFunc;
10810
10811 functions
10812 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10813 << new QgsWithVariableExpressionFunction()
10814 << new QgsStaticExpressionFunction(
10815 u"raster_value"_s,
10816 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10817 fcnRasterValue,
10818 u"Rasters"_s
10819 )
10820 << new QgsStaticExpressionFunction(
10821 u"raster_attributes"_s,
10822 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10823 fcnRasterAttributes,
10824 u"Rasters"_s
10825 )
10826
10827 // functions for arrays
10828 << new QgsArrayForeachExpressionFunction()
10829 << new QgsArrayFilterExpressionFunction()
10830 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10831 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10832 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10833 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10834 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10835 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayAll, u"Arrays"_s )
10836 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10837 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10838 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10839 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10840 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10841 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10842 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10843 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10844 << 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 )
10845 << 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 )
10846 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10847 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10848 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10849 << new QgsStaticExpressionFunction(
10850 u"array_insert"_s,
10851 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10852 fcnArrayInsert,
10853 u"Arrays"_s
10854 )
10855 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10856 << new QgsStaticExpressionFunction(
10857 u"array_remove_all"_s,
10858 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10859 fcnArrayRemoveAll,
10860 u"Arrays"_s,
10861 QString(),
10862 false,
10863 QSet<QString>(),
10864 false,
10865 QStringList(),
10866 true
10867 )
10868 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10869 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"priority"_s ), fcnArrayPrioritize, u"Arrays"_s )
10870 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10871 << new QgsStaticExpressionFunction(
10872 u"array_slice"_s,
10873 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ),
10874 fcnArraySlice,
10875 u"Arrays"_s
10876 )
10877 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10878 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10879 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10880 << new QgsStaticExpressionFunction(
10881 u"array_to_string"_s,
10883 << QgsExpressionFunction::Parameter( u"array"_s )
10884 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10885 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10886 fcnArrayToString,
10887 u"Arrays"_s
10888 )
10889 << new QgsStaticExpressionFunction(
10890 u"string_to_array"_s,
10892 << QgsExpressionFunction::Parameter( u"string"_s )
10893 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10894 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10895 fcnStringToArray,
10896 u"Arrays"_s
10897 )
10898 << new QgsStaticExpressionFunction(
10899 u"generate_series"_s,
10900 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ),
10901 fcnGenerateSeries,
10902 u"Arrays"_s
10903 )
10904 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10905
10906 //functions for maps
10907 << 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 )
10908 << 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 )
10909 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10910 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10911 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10912 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10913 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10914 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10915 << 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 )
10916 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10917 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10918 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10919 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"prefix"_s ), fcnMapPrefixKeys, u"Maps"_s )
10920 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlTable, u"Maps"_s )
10921 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlDefinitionList, u"Maps"_s )
10922 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnToFormUrlEncode, u"Maps"_s )
10923
10924 ;
10925
10927
10928 //QgsExpression has ownership of all built-in functions
10929 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10930 {
10931 *sOwnedFunctions() << func;
10932 *sBuiltinFunctions() << func->name();
10933 sBuiltinFunctions()->append( func->aliases() );
10934 }
10935 }
10936 return functions;
10937}
10938
10939bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10940{
10941 int fnIdx = functionIndex( function->name() );
10942 if ( fnIdx != -1 )
10943 {
10944 return false;
10945 }
10946
10947 QMutexLocker locker( &sFunctionsMutex );
10948 sFunctions()->append( function );
10949 if ( transferOwnership )
10950 sOwnedFunctions()->append( function );
10951
10952 return true;
10953}
10954
10955bool QgsExpression::unregisterFunction( const QString &name )
10956{
10957 // You can never override the built in functions.
10958 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10959 {
10960 return false;
10961 }
10962 int fnIdx = functionIndex( name );
10963 if ( fnIdx != -1 )
10964 {
10965 QMutexLocker locker( &sFunctionsMutex );
10966 sFunctions()->removeAt( fnIdx );
10967 sFunctionIndexMap.clear();
10968 return true;
10969 }
10970 return false;
10971}
10972
10974{
10975 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10976 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10977 {
10978 sBuiltinFunctions()->removeAll( func->name() );
10979 for ( const QString &alias : func->aliases() )
10981 sBuiltinFunctions()->removeAll( alias );
10982 }
10983
10984 sFunctions()->removeAll( func );
10985 }
10986
10987 qDeleteAll( *sOwnedFunctions() );
10988 sOwnedFunctions()->clear();
10990
10991const QStringList &QgsExpression::BuiltinFunctions()
10992{
10993 if ( sBuiltinFunctions()->isEmpty() )
10994 {
10995 Functions(); // this method builds the gmBuiltinFunctions as well
10996 }
10997 return *sBuiltinFunctions();
10998}
10999
11002 u"array_foreach"_s, // skip-keyword-check
11003 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ),
11004 u"Arrays"_s
11005 )
11006{}
11007
11009{
11010 bool isStatic = false;
11011
11012 QgsExpressionNode::NodeList *args = node->args();
11014 if ( args->count() < 2 )
11015 return false;
11016
11017 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11018 {
11019 isStatic = true;
11020 }
11021 return isStatic;
11022}
11023
11025{
11026 Q_UNUSED( node )
11027 QVariantList result;
11028
11029 if ( args->count() < 2 )
11030 // error
11031 return result;
11032
11033 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11034
11035 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11036 std::unique_ptr< QgsExpressionContext > tempContext;
11037 if ( !subContext )
11038 {
11039 tempContext = std::make_unique< QgsExpressionContext >();
11040 subContext = tempContext.get();
11041 }
11042
11043 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11044 subContext->appendScope( subScope );
11045
11046 int i = 0;
11047 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
11048 {
11049 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
11050 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
11051 result << args->at( 1 )->eval( parent, subContext );
11052 }
11053
11054 if ( context )
11055 delete subContext->popScope();
11056
11057 return result;
11058}
11059
11060QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11062 // This is a dummy function, all the real handling is in run
11063 Q_UNUSED( values )
11064 Q_UNUSED( context )
11065 Q_UNUSED( parent )
11066 Q_UNUSED( node )
11067
11068 Q_ASSERT( false );
11069 return QVariant();
11070}
11071
11073{
11074 QgsExpressionNode::NodeList *args = node->args();
11075
11076 if ( args->count() < 2 )
11077 // error
11078 return false;
11079
11080 args->at( 0 )->prepare( parent, context );
11081
11082 QgsExpressionContext subContext;
11083 if ( context )
11084 subContext = *context;
11087 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11088 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
11089 subContext.appendScope( subScope );
11090
11091 args->at( 1 )->prepare( parent, &subContext );
11092
11093 return true;
11094}
11095
11097 : 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 )
11098{}
11099
11101{
11102 bool isStatic = false;
11103
11104 QgsExpressionNode::NodeList *args = node->args();
11106 if ( args->count() < 2 )
11107 return false;
11108
11109 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11110 {
11111 isStatic = true;
11112 }
11113 return isStatic;
11114}
11115
11117{
11118 Q_UNUSED( node )
11119 QVariantList result;
11120
11121 if ( args->count() < 2 )
11122 // error
11123 return result;
11124
11125 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
11126
11127 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
11128 std::unique_ptr< QgsExpressionContext > tempContext;
11129 if ( !subContext )
11130 {
11131 tempContext = std::make_unique< QgsExpressionContext >();
11132 subContext = tempContext.get();
11133 }
11134
11135 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
11136 subContext->appendScope( subScope );
11137
11138 int limit = 0;
11139 if ( args->count() >= 3 )
11140 {
11141 const QVariant limitVar = args->at( 2 )->eval( parent, context );
11142
11143 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
11144 {
11145 limit = limitVar.toInt();
11146 }
11147 else
11148 {
11149 return result;
11150 }
11151 }
11152
11153 for ( const QVariant &value : array )
11154 {
11155 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
11156 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
11157 {
11158 result << value;
11159
11160 if ( limit > 0 && limit == result.size() )
11161 break;
11162 }
11163 }
11164
11165 if ( context )
11166 delete subContext->popScope();
11167
11168 return result;
11169}
11170
11171QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11173 // This is a dummy function, all the real handling is in run
11174 Q_UNUSED( values )
11175 Q_UNUSED( context )
11176 Q_UNUSED( parent )
11177 Q_UNUSED( node )
11178
11179 Q_ASSERT( false );
11180 return QVariant();
11181}
11182
11184{
11185 QgsExpressionNode::NodeList *args = node->args();
11186
11187 if ( args->count() < 2 )
11188 // error
11189 return false;
11190
11191 args->at( 0 )->prepare( parent, context );
11192
11193 QgsExpressionContext subContext;
11194 if ( context )
11195 subContext = *context;
11196
11198 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
11199 subContext.appendScope( subScope );
11200
11201 args->at( 1 )->prepare( parent, &subContext );
11202
11203 return true;
11204}
11206 : 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 )
11207{}
11208
11210{
11211 bool isStatic = false;
11212
11213 QgsExpressionNode::NodeList *args = node->args();
11214
11215 if ( args->count() < 3 )
11216 return false;
11217
11218 // We only need to check if the node evaluation is static, if both - name and value - are static.
11219 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
11220 {
11221 QVariant name = args->at( 0 )->eval( parent, context );
11222 QVariant value = args->at( 1 )->eval( parent, context );
11224 // Temporarily append a new scope to provide the variable
11225 appendTemporaryVariable( context, name.toString(), value );
11226 if ( args->at( 2 )->isStatic( parent, context ) )
11227 isStatic = true;
11228 popTemporaryVariable( context );
11229 }
11230
11231 return isStatic;
11232}
11233
11235{
11236 Q_UNUSED( node )
11237 QVariant result;
11238
11239 if ( args->count() < 3 )
11240 // error
11241 return result;
11242
11243 QVariant name = args->at( 0 )->eval( parent, context );
11244 QVariant value = args->at( 1 )->eval( parent, context );
11245
11246 const QgsExpressionContext *updatedContext = context;
11247 std::unique_ptr< QgsExpressionContext > tempContext;
11248 if ( !updatedContext )
11249 {
11250 tempContext = std::make_unique< QgsExpressionContext >();
11251 updatedContext = tempContext.get();
11253
11254 appendTemporaryVariable( updatedContext, name.toString(), value );
11255 result = args->at( 2 )->eval( parent, updatedContext );
11256
11257 if ( context )
11258 popTemporaryVariable( updatedContext );
11259
11260 return result;
11261}
11262
11263QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
11265 // This is a dummy function, all the real handling is in run
11266 Q_UNUSED( values )
11267 Q_UNUSED( context )
11268 Q_UNUSED( parent )
11269 Q_UNUSED( node )
11270
11271 Q_ASSERT( false );
11272 return QVariant();
11273}
11274
11276{
11277 QgsExpressionNode::NodeList *args = node->args();
11278
11279 if ( args->count() < 3 )
11280 // error
11281 return false;
11282
11283 QVariant name = args->at( 0 )->prepare( parent, context );
11284 QVariant value = args->at( 1 )->prepare( parent, context );
11285
11286 const QgsExpressionContext *updatedContext = context;
11287 std::unique_ptr< QgsExpressionContext > tempContext;
11288 if ( !updatedContext )
11289 {
11290 tempContext = std::make_unique< QgsExpressionContext >();
11291 updatedContext = tempContext.get();
11292 }
11293
11294 appendTemporaryVariable( updatedContext, name.toString(), value );
11295 args->at( 2 )->prepare( parent, updatedContext );
11296
11297 if ( context )
11298 popTemporaryVariable( updatedContext );
11299
11300 return true;
11301}
11302
11303void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
11304{
11305 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11306 delete updatedContext->popScope();
11307}
11308
11309void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
11310{
11311 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
11312 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
11313
11314 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
11315 updatedContext->appendScope( scope );
11316}
GeometryBackend
Geometry backend for QgsGeometry.
Definition qgis.h:2299
@ GEOS
Use GEOS implementation.
Definition qgis.h:2301
@ QGIS
Use internal implementation.
Definition qgis.h:2300
@ Left
Buffer to left of line.
Definition qgis.h:2249
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3507
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3509
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3508
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3510
@ Success
Operation succeeded.
Definition qgis.h:2193
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3219
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2360
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
JoinStyle
Join styles for buffers.
Definition qgis.h:2273
@ Bevel
Use beveled joins.
Definition qgis.h:2276
@ Round
Use rounded joins.
Definition qgis.h:2274
@ Miter
Use mitered joins.
Definition qgis.h:2275
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6612
@ StdDev
Standard deviation.
Definition qgis.h:6619
@ NoStatistic
No statistic.
Definition qgis.h:6613
@ 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:2260
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2262
@ Round
Round cap.
Definition qgis.h:2261
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2263
Aggregate
Available aggregates to calculate.
Definition qgis.h:6489
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6506
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6503
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6496
@ Median
Median of values (numeric fields only).
Definition qgis.h:6497
@ Max
Max of values.
Definition qgis.h:6494
@ Min
Min of values.
Definition qgis.h:6493
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6507
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6500
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6511
@ Sum
Sum of values.
Definition qgis.h:6495
@ Minority
Minority of values.
Definition qgis.h:6501
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6492
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6510
@ Majority
Majority of values.
Definition qgis.h:6502
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6499
@ Count
Count.
Definition qgis.h:6490
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6504
@ CountDistinct
Number of distinct values.
Definition qgis.h:6491
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6508
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6509
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6505
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3492
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3495
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3497
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3496
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3494
@ NoRule
No special rule.
Definition qgis.h:3493
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2344
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2345
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2346
@ Point
Point.
Definition qgis.h:296
@ PointM
PointM.
Definition qgis.h:329
@ PointZ
PointZ.
Definition qgis.h:313
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ PointZM
PointZM.
Definition qgis.h:345
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:46
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:282
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:277
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:41
Defines a QGIS exception class.
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
An abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
QgsExpressionFunction(const QString &fnname, int params, const QString &group, const QString &helpText=QString(), bool lazyEval=false, bool handlesNull=false, bool isContextual=false)
Constructor for function which uses unnamed parameters.
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
const QString helpText() const
The help text for the function.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes its value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions).
friend class QgsExpressionNodeFunction
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsExpression(const QString &expr)
Creates a new expression based on the provided string.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:65
QgsFeatureId id
Definition qgsfeature.h:63
QgsGeometry geometry
Definition qgsfeature.h:66
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:754
Container of fields for a vector layer.
Definition qgsfields.h: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:175
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2959
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
A representation of the interval between two datetime values.
Definition qgsinterval.h:52
bool isValid() const
Returns true if the interval is valid.
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
double years() const
Returns the interval duration in years (based on an average year length).
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Represents a model of the Earth's magnetic field.
static bool fieldComponentsWithTimeDerivatives(double Bx, double By, double Bz, double Bxt, double Byt, double Bzt, double &H, double &F, double &D, double &I, double &Ht, double &Ft, double &Dt, double &It)
Compute various quantities dependent on a magnetic field and their rates of change.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
virtual Q_INVOKABLE QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:86
QgsLayerMetadata metadata
Definition qgsmaplayer.h:89
Qgis::LayerType type
Definition qgsmaplayer.h:93
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString mapTipTemplate
Definition qgsmaplayer.h:96
Implementation of a geometry simplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:245
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:737
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:603
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:460
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:138
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
double m
Definition qgspoint.h:59
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:749
double y
Definition qgspoint.h:57
QgsRelationManager * relationManager
Definition qgsproject.h:125
static QgsProject * instance()
Returns the QgsProject singleton instance.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
bool isRamp() const
Returns true if the field carries a color ramp component information (RedMin/RedMax,...
bool isColor() const
Returns true if the field carries a color component (Red, Green, Blue and optionally Alpha) informati...
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
void grow(double delta)
Grows the rectangle in place by the specified amount.
double yMaximum
QgsPointXY center
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices).
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
Q_INVOKABLE QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString unaccent(const QString &input)
Removes accents and other diacritical marks from a string, replacing accented characters with their u...
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance).
Definition qgsstyle.cpp:541
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:164
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
Decodes a string to a color.
static QString encodeColor(const QColor &color)
Lossy-encodes a color to a string.
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:596
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:7743
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8086
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8085
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7582
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7488
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