QGIS API Documentation 4.1.0-Master (60fea48833c)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
45#include "qgsmagneticmodel.h"
47#include "qgsmessagelog.h"
48#include "qgsmultilinestring.h"
49#include "qgsmultipoint.h"
50#include "qgsogcutils.h"
51#include "qgspolygon.h"
52#include "qgsproviderregistry.h"
53#include "qgsquadrilateral.h"
54#include "qgsrasterbandstats.h"
55#include "qgsrasterlayer.h"
56#include "qgsregularpolygon.h"
57#include "qgsspatialindex.h"
58#include "qgsstringutils.h"
59#include "qgsstyle.h"
60#include "qgssymbollayerutils.h"
61#include "qgsthreadingutils.h"
62#include "qgstransaction.h"
63#include "qgstriangle.h"
64#include "qgsunittypes.h"
65#include "qgsvariantutils.h"
66#include "qgsvectorlayer.h"
68#include "qgsvectorlayerutils.h"
69
70#include <QCryptographicHash>
71#include <QMimeDatabase>
72#include <QProcessEnvironment>
73#include <QRegularExpression>
74#include <QString>
75#include <QUrlQuery>
76#include <QUuid>
77
78using namespace Qt::StringLiterals;
79
80typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
81
83Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
85
88Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
89
90const QString QgsExpressionFunction::helpText() const
91{
92 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
93}
94
96{
97 Q_UNUSED( node )
98 // evaluate arguments
99 QVariantList argValues;
100 if ( args )
101 {
102 int arg = 0;
103 const QList< QgsExpressionNode * > argList = args->list();
104 argValues.reserve( argList.size() );
105 for ( QgsExpressionNode *n : argList )
106 {
107 QVariant v;
108 if ( lazyEval() )
109 {
110 // Pass in the node for the function to eval as it needs.
111 v = QVariant::fromValue( n );
112 }
113 else
114 {
115 v = n->eval( parent, context );
117 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
118 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
119 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
120 }
121 argValues.append( v );
122 arg++;
123 }
124 }
125
126 return func( argValues, context, parent, node );
127}
128
130{
131 Q_UNUSED( node )
132 return true;
133}
134
136{
137 return QStringList();
138}
139
141{
142 Q_UNUSED( parent )
143 Q_UNUSED( context )
144 Q_UNUSED( node )
145 return false;
146}
147
149{
150 Q_UNUSED( parent )
151 Q_UNUSED( context )
152 Q_UNUSED( node )
153 return true;
154}
155
157{
158 Q_UNUSED( node )
159 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
160}
161
163{
164 return mGroups.isEmpty() ? false : mGroups.contains( u"deprecated"_s );
165}
166
168{
169 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
170}
171
173{
174 return mHandlesNull;
175}
176
177// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
180 const QString &fnname,
182 FcnEval fcn,
183 const QString &group,
184 const QString &helpText,
185 const std::function< bool( const QgsExpressionNodeFunction *node ) > &usesGeometry,
186 const std::function< QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
187 bool lazyEval,
188 const QStringList &aliases,
189 bool handlesNull
190)
191 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
192 , mFnc( fcn )
193 , mAliases( aliases )
194 , mUsesGeometry( false )
195 , mUsesGeometryFunc( usesGeometry )
196 , mReferencedColumnsFunc( referencedColumns )
197{}
199
201{
202 return mAliases;
203}
204
206{
207 if ( mUsesGeometryFunc )
208 return mUsesGeometryFunc( node );
209 else
210 return mUsesGeometry;
211}
212
214{
215 mUsesGeometryFunc = usesGeometry;
216}
217
219{
220 if ( mReferencedColumnsFunc )
221 return mReferencedColumnsFunc( node );
222 else
223 return mReferencedColumns;
224}
225
227{
228 if ( mIsStaticFunc )
229 return mIsStaticFunc( node, parent, context );
230 else
231 return mIsStatic;
232}
233
235{
236 if ( mPrepareFunc )
237 return mPrepareFunc( node, parent, context );
238
239 return true;
240}
241
243{
244 mIsStaticFunc = isStatic;
245}
246
248{
249 mIsStaticFunc = nullptr;
250 mIsStatic = isStatic;
251}
252
253void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
254{
255 mPrepareFunc = prepareFunc;
256}
257
259{
260 if ( node && node->args() )
261 {
262 const QList< QgsExpressionNode * > argList = node->args()->list();
263 for ( QgsExpressionNode *argNode : argList )
264 {
265 if ( !argNode->isStatic( parent, context ) )
266 return false;
267 }
268 }
269
270 return true;
271}
272
273static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
274{
275 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
276 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
277 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
278
279 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
280 return QVariant();
281
282 QVariantList array;
283 int length = 1;
284
285 array << start;
286 double current = start + step;
287 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
288 {
289 array << current;
290 current += step;
291 length++;
292 }
293
294 return array;
295}
296
297static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
298{
299 if ( !context )
300 return QVariant();
301
302 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
303
304 if ( name == "feature"_L1 )
305 {
306 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
307 }
308 else if ( name == "id"_L1 )
309 {
310 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
311 }
312 else if ( name == "geometry"_L1 )
313 {
314 if ( !context->hasFeature() )
315 return QVariant();
316
317 const QgsFeature feature = context->feature();
318 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
319 }
320 else
321 {
322 return context->variable( name );
323 }
324}
325
326static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
327{
328 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
329 return QgsExpression::replaceExpressionText( templateString, context );
330}
331
332static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
333{
334 if ( !context )
335 return QVariant();
336
337 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
338 QgsExpression expression( expString );
339 return expression.evaluate( context );
340}
341
342static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
343{
344 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
345 return QVariant( std::sqrt( x ) );
346}
347
348static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
349{
350 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
351 return QVariant( std::fabs( val ) );
352}
353
354static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
355{
356 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
357 return ( deg * M_PI ) / 180;
358}
359static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
360{
361 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
362 return ( 180 * rad ) / M_PI;
363}
364static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
365{
366 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
367 return QVariant( std::sin( x ) );
368}
369static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
370{
371 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
372 return QVariant( std::cos( x ) );
373}
374static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
375{
376 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
377 return QVariant( std::tan( x ) );
378}
379static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
380{
381 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
382 return QVariant( std::asin( x ) );
383}
384static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
385{
386 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
387 return QVariant( std::acos( x ) );
388}
389static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
390{
391 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
392 return QVariant( std::atan( x ) );
393}
394static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
395{
396 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
397 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
398 return QVariant( std::atan2( y, x ) );
399}
400static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
401{
402 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
403 return QVariant( std::exp( x ) );
404}
405static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
406{
407 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
408 if ( x <= 0 )
409 return QVariant();
410 return QVariant( std::log( x ) );
411}
412static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
413{
414 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
415 if ( x <= 0 )
416 return QVariant();
417 return QVariant( log10( x ) );
418}
419static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
420{
421 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
422 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
423 if ( x <= 0 || b <= 0 )
424 return QVariant();
425 return QVariant( std::log( x ) / std::log( b ) );
426}
427static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
428{
429 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
430 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
431 if ( max < min )
432 return QVariant();
433
434 std::random_device rd;
435 std::mt19937_64 generator( rd() );
436
437 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
438 {
439 quint32 seed;
440 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
441 {
442 // if seed can be converted to int, we use as is
443 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
444 }
445 else
446 {
447 // if not, we hash string representation to int
448 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
449 std::hash<std::string> hasher;
450 seed = hasher( seedStr.toStdString() );
451 }
452 generator.seed( seed );
453 }
454
455 // Return a random double in the range [min, max] (inclusive)
456 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
457 return QVariant( min + f * ( max - min ) );
458}
459static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
460{
461 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
462 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
463 if ( max < min )
464 return QVariant();
465
466 std::random_device rd;
467 std::mt19937_64 generator( rd() );
468
469 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
470 {
471 quint32 seed;
472 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
473 {
474 // if seed can be converted to int, we use as is
475 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
476 }
477 else
478 {
479 // if not, we hash string representation to int
480 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
481 std::hash<std::string> hasher;
482 seed = hasher( seedStr.toStdString() );
483 }
484 generator.seed( seed );
485 }
486
487 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
488 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
489 return QVariant( randomInteger );
490
491 // Prevent wrong conversion of QVariant. See #36412
492 return QVariant( int( randomInteger ) );
493}
494
495static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
496{
497 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
498 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
499 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
500 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
501 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
502
503 if ( domainMin >= domainMax )
504 {
505 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
506 return QVariant();
507 }
508
509 // outside of domain?
510 if ( val >= domainMax )
511 {
512 return rangeMax;
513 }
514 else if ( val <= domainMin )
515 {
516 return rangeMin;
517 }
518
519 // calculate linear scale
520 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
521 double c = rangeMin - ( domainMin * m );
522
523 // Return linearly scaled value
524 return QVariant( m * val + c );
525}
526
527static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
528{
529 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
530 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
531 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
532 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
533 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
534 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
535
536 if ( domainMin >= domainMax )
537 {
538 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
539 return QVariant();
540 }
541 if ( exponent <= 0 )
542 {
543 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
544 return QVariant();
545 }
546
547 // outside of domain?
548 if ( val >= domainMax )
549 {
550 return rangeMax;
551 }
552 else if ( val <= domainMin )
553 {
554 return rangeMin;
555 }
556
557 // Return polynomially scaled value
558 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
559}
560
561static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
562{
563 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
564 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
565 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
566 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
567 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
568 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
569
570 if ( domainMin >= domainMax )
571 {
572 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
573 return QVariant();
574 }
575 if ( exponent <= 0 )
576 {
577 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
578 return QVariant();
579 }
580
581 // outside of domain?
582 if ( val >= domainMax )
583 {
584 return rangeMax;
585 }
586 else if ( val <= domainMin )
587 {
588 return rangeMin;
589 }
590
591 // Return exponentially scaled value
592 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
593 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
594}
595
596static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
597{
598 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
599 double maxVal = std::numeric_limits<double>::quiet_NaN();
600 for ( const QVariant &val : values )
601 {
602 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
603 if ( std::isnan( maxVal ) )
604 {
605 maxVal = testVal;
606 }
607 else if ( !std::isnan( testVal ) )
608 {
609 maxVal = std::max( maxVal, testVal );
610 }
611 }
612
613 if ( !std::isnan( maxVal ) )
614 {
615 result = QVariant( maxVal );
616 }
617 return result;
618}
619
620static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
621{
622 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
623 double minVal = std::numeric_limits<double>::quiet_NaN();
624 for ( const QVariant &val : values )
625 {
626 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
627 if ( std::isnan( minVal ) )
628 {
629 minVal = testVal;
630 }
631 else if ( !std::isnan( testVal ) )
632 {
633 minVal = std::min( minVal, testVal );
634 }
635 }
636
637 if ( !std::isnan( minVal ) )
638 {
639 result = QVariant( minVal );
640 }
641 return result;
642}
643
644static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
645{
646 //lazy eval, so we need to evaluate nodes now
647
648 //first node is layer id or name
649 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
651 QVariant value = node->eval( parent, context );
653
654 // TODO this expression function is NOT thread safe
656 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
658 if ( !vl )
659 {
660 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
661 return QVariant();
662 }
663
664 // second node is aggregate type
665 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
667 value = node->eval( parent, context );
669 bool ok = false;
670 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
671 if ( !ok )
672 {
673 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
674 return QVariant();
675 }
676
677 // third node is subexpression (or field name)
678 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
680 QString subExpression = node->dump();
681
683 //optional forth node is filter
684 if ( values.count() > 3 )
685 {
686 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
688 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
689 if ( !nl || nl->value().isValid() )
690 parameters.filter = node->dump();
691 }
692
693 //optional fifth node is concatenator
694 if ( values.count() > 4 )
695 {
696 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
698 value = node->eval( parent, context );
700 parameters.delimiter = value.toString();
701 }
702
703 //optional sixth node is order by
704 QString orderBy;
705 if ( values.count() > 5 )
706 {
707 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
709 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
710 if ( !nl || nl->value().isValid() )
711 {
712 orderBy = node->dump();
713 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
714 }
715 }
716
717 QString aggregateError;
718 QVariant result;
719 if ( context )
720 {
721 QString cacheKey;
722 QgsExpression subExp( subExpression );
723 QgsExpression filterExp( parameters.filter );
724
725 const QSet< QString > filterVars = filterExp.referencedVariables();
726 const QSet< QString > subExpVars = subExp.referencedVariables();
727 QSet<QString> allVars = filterVars + subExpVars;
728
729 bool isStatic = true;
730 if ( filterVars.contains( u"parent"_s ) || filterVars.contains( QString() ) || subExpVars.contains( u"parent"_s ) || subExpVars.contains( QString() ) )
731 {
732 isStatic = false;
733 }
734 else
735 {
736 for ( const QString &varName : allVars )
737 {
738 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
739 if ( scope && !scope->isStatic( varName ) )
740 {
741 isStatic = false;
742 break;
743 }
744 }
745 }
746
747 if ( isStatic && !parameters.orderBy.isEmpty() )
748 {
749 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
750 {
751 const QgsExpression &orderByExpression { orderByClause.expression() };
752 if ( orderByExpression.referencedVariables().contains( u"parent"_s ) || orderByExpression.referencedVariables().contains( QString() ) )
753 {
754 isStatic = false;
755 break;
756 }
757 }
758 }
759
760 if ( !isStatic )
761 {
762 bool ok = false;
763 const QString contextHash = context->uniqueHash( ok, allVars );
764 if ( ok )
765 {
766 cacheKey = u"aggfcn:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
767 }
768 }
769 else
770 {
771 cacheKey = u"aggfcn:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
772 }
773
774 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
775 {
776 return context->cachedValue( cacheKey );
777 }
778
779 QgsExpressionContext subContext( *context );
781 subScope->setVariable( u"parent"_s, context->feature(), true );
782 subContext.appendScope( subScope );
783 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
784
785 if ( ok && !cacheKey.isEmpty() )
786 {
787 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
788 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
789 // associated with it's calculation!
790 context->setCachedValue( cacheKey, result );
791 }
792 }
793 else
794 {
795 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
796 }
797 if ( !ok )
798 {
799 if ( !aggregateError.isEmpty() )
800 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
801 else
802 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
803 return QVariant();
804 }
805
806 return result;
807}
808
809static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
810{
811 if ( !context )
812 {
813 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
814 return QVariant();
815 }
816
817 // first step - find current layer
818
819 // TODO this expression function is NOT thread safe
821 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
823 if ( !vl )
824 {
825 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
826 return QVariant();
827 }
828
829 //lazy eval, so we need to evaluate nodes now
830
831 //first node is relation name
832 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
834 QVariant value = node->eval( parent, context );
836 QString relationId = value.toString();
837 // check relation exists
838 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
839 if ( !relation.isValid() || relation.referencedLayer() != vl )
840 {
841 // check for relations by name
842 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
843 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
844 {
845 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
846 return QVariant();
847 }
848 else
849 {
850 relation = relations.at( 0 );
851 }
852 }
853
854 QgsVectorLayer *childLayer = relation.referencingLayer();
855
856 // second node is aggregate type
857 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
859 value = node->eval( parent, context );
861 bool ok = false;
862 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
863 if ( !ok )
864 {
865 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
866 return QVariant();
867 }
868
869 //third node is subexpression (or field name)
870 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
872 QString subExpression = node->dump();
873
874 //optional fourth node is concatenator
876 if ( values.count() > 3 )
877 {
878 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
880 value = node->eval( parent, context );
882 parameters.delimiter = value.toString();
883 }
884
885 //optional fifth node is order by
886 QString orderBy;
887 if ( values.count() > 4 )
888 {
889 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
891 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
892 if ( !nl || nl->value().isValid() )
893 {
894 orderBy = node->dump();
895 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
896 }
897 }
898
899 if ( !context->hasFeature() )
900 return QVariant();
901 QgsFeature f = context->feature();
902
903 parameters.filter = relation.getRelatedFeaturesFilter( f );
904
905 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 );
906 if ( context->hasCachedValue( cacheKey ) )
907 return context->cachedValue( cacheKey );
908
909 QVariant result;
910 ok = false;
911
912
913 QgsExpressionContext subContext( *context );
914 QString error;
915 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
916
917 if ( !ok )
918 {
919 if ( !error.isEmpty() )
920 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
921 else
922 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
923 return QVariant();
924 }
925
926 // cache value
927 context->setCachedValue( cacheKey, result );
928 return result;
929}
930
931
932static QVariant fcnAggregateGeneric(
933 Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1
934)
935{
936 if ( !context )
937 {
938 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
939 return QVariant();
940 }
941
942 // first step - find current layer
943
944 // TODO this expression function is NOT thread safe
946 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
948 if ( !vl )
949 {
950 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
951 return QVariant();
952 }
953
954 //lazy eval, so we need to evaluate nodes now
955
956 //first node is subexpression (or field name)
957 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
959 QString subExpression = node->dump();
960
961 //optional second node is group by
962 QString groupBy;
963 if ( values.count() > 1 )
964 {
965 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
967 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
968 if ( !nl || nl->value().isValid() )
969 groupBy = node->dump();
970 }
971
972 //optional third node is filter
973 if ( values.count() > 2 )
974 {
975 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
977 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
978 if ( !nl || nl->value().isValid() )
979 parameters.filter = node->dump();
980 }
981
982 //optional order by node, if supported
983 QString orderBy;
984 if ( orderByPos >= 0 && values.count() > orderByPos )
985 {
986 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
988 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
989 if ( !nl || nl->value().isValid() )
990 {
991 orderBy = node->dump();
992 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
993 }
994 }
995
996 // build up filter with group by
997
998 // find current group by value
999 if ( !groupBy.isEmpty() )
1000 {
1001 QgsExpression groupByExp( groupBy );
1002 QVariant groupByValue = groupByExp.evaluate( context );
1003 QString groupByClause = u"%1 %2 %3"_s.arg( groupBy, QgsVariantUtils::isNull( groupByValue ) ? u"is"_s : u"="_s, QgsExpression::quotedValue( groupByValue ) );
1004 if ( !parameters.filter.isEmpty() )
1005 parameters.filter = u"(%1) AND (%2)"_s.arg( parameters.filter, groupByClause );
1006 else
1007 parameters.filter = groupByClause;
1008 }
1009
1010 QgsExpression subExp( subExpression );
1011 QgsExpression filterExp( parameters.filter );
1012
1013 bool isStatic = true;
1014 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1015 for ( const QString &varName : refVars )
1016 {
1017 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1018 if ( scope && !scope->isStatic( varName ) )
1019 {
1020 isStatic = false;
1021 break;
1022 }
1023 }
1024
1025 QString cacheKey;
1026 if ( !isStatic )
1027 {
1028 bool ok = false;
1029 const QString contextHash = context->uniqueHash( ok, refVars );
1030 if ( ok )
1031 {
1032 cacheKey = u"agg:%1:%2:%3:%4:%5:%6"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy, contextHash );
1033 }
1034 }
1035 else
1036 {
1037 cacheKey = u"agg:%1:%2:%3:%4:%5"_s.arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1038 }
1039
1040 if ( context->hasCachedValue( cacheKey ) )
1041 return context->cachedValue( cacheKey );
1042
1043 QVariant result;
1044 bool ok = false;
1045
1046 QgsExpressionContext subContext( *context );
1048 subScope->setVariable( u"parent"_s, context->feature(), true );
1049 subContext.appendScope( subScope );
1050 QString error;
1051 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1052
1053 if ( !ok )
1054 {
1055 if ( !error.isEmpty() )
1056 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1057 else
1058 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1059 return QVariant();
1060 }
1061
1062 // cache value
1063 context->setCachedValue( cacheKey, result );
1064 return result;
1065}
1066
1067
1068static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1069{
1070 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1071}
1072
1073static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1074{
1075 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1076}
1077
1078static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1079{
1080 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1081}
1082
1083static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1084{
1085 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1086}
1087
1088static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1089{
1090 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1091}
1092
1093static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1094{
1095 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1096}
1097
1098static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1099{
1100 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1101}
1102
1103static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1104{
1105 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1106}
1107
1108static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1109{
1110 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1111}
1112
1113static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1114{
1115 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1116}
1117
1118static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1119{
1120 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1121}
1122
1123static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1124{
1125 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1126}
1127
1128static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1129{
1130 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1131}
1132
1133static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1134{
1135 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1136}
1137
1138static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1139{
1140 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1141}
1142
1143static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1144{
1145 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1146}
1147
1148static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1149{
1150 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1151}
1152
1153static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1154{
1155 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1156}
1157
1158static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1159{
1161
1162 //fourth node is concatenator
1163 if ( values.count() > 3 )
1164 {
1165 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1167 QVariant value = node->eval( parent, context );
1169 parameters.delimiter = value.toString();
1170 }
1171
1172 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1173}
1174
1175static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1176{
1178
1179 //fourth node is concatenator
1180 if ( values.count() > 3 )
1181 {
1182 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1184 QVariant value = node->eval( parent, context );
1186 parameters.delimiter = value.toString();
1187 }
1188
1189 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1190}
1191
1192static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1193{
1194 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1195}
1196
1197static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1198{
1199 if ( !context )
1200 return QVariant();
1201
1202 QVariant scale = context->variable( u"map_scale"_s );
1203 bool ok = false;
1204 if ( QgsVariantUtils::isNull( scale ) )
1205 return QVariant();
1206
1207 const double v = scale.toDouble( &ok );
1208 if ( ok )
1209 return v;
1210 return QVariant();
1211}
1212
1213static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1214{
1215 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1216 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1217 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1218
1219 // force testValue to sit inside the range specified by the min and max value
1220 if ( testValue <= minValue )
1221 {
1222 return QVariant( minValue );
1223 }
1224 else if ( testValue >= maxValue )
1225 {
1226 return QVariant( maxValue );
1227 }
1228 else
1229 {
1230 return QVariant( testValue );
1231 }
1232}
1233
1234static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1235{
1236 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1237 return QVariant( std::floor( x ) );
1238}
1239
1240static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1241{
1242 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1243 return QVariant( std::ceil( x ) );
1244}
1245
1246static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1247{
1248 const QVariant value = values.at( 0 );
1249 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1250 {
1251 return QVariant( false );
1252 }
1253 else if ( value.userType() == QMetaType::QString )
1254 {
1255 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1256 return QVariant( !value.toString().isEmpty() );
1257 }
1258 else if ( QgsExpressionUtils::isList( value ) )
1259 {
1260 return !value.toList().isEmpty();
1261 }
1262 return QVariant( value.toBool() );
1263}
1264static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1265{
1266 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1267}
1268static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1269{
1270 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1271}
1272static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1273{
1274 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1275}
1276
1277static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1278{
1279 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1280 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1281 if ( format.isEmpty() && !language.isEmpty() )
1282 {
1283 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1284 return QVariant( QDateTime() );
1285 }
1286
1287 if ( format.isEmpty() && language.isEmpty() )
1288 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1289
1290 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1291 QLocale locale = QLocale();
1292 if ( !language.isEmpty() )
1293 {
1294 locale = QLocale( language );
1295 }
1296
1297 QDateTime datetime = locale.toDateTime( datetimestring, format );
1298 if ( !datetime.isValid() )
1299 {
1300 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1301 datetime = QDateTime();
1302 }
1303 return QVariant( datetime );
1304}
1305
1306static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1307{
1308 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1309 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1310 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1311
1312 const QDate date( year, month, day );
1313 if ( !date.isValid() )
1314 {
1315 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1316 return QVariant();
1317 }
1318 return QVariant( date );
1319}
1320
1321static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1322{
1323 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1324 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1325 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1326
1327 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1328 if ( !time.isValid() )
1329 {
1330 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1331 return QVariant();
1332 }
1333 return QVariant( time );
1334}
1335
1336static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1337{
1338 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1339 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1340 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1341 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1342 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1343 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1344
1345 const QDate date( year, month, day );
1346 if ( !date.isValid() )
1347 {
1348 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1349 return QVariant();
1350 }
1351 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1352 if ( !time.isValid() )
1353 {
1354 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1355 return QVariant();
1356 }
1357 return QVariant( QDateTime( date, time ) );
1358}
1359
1360static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1361{
1362 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1363
1364 QTimeZone tz;
1365
1366#if QT_FEATURE_timezone > 0
1367 if ( !timeZoneId.isEmpty() )
1368 {
1369 tz = QTimeZone( timeZoneId.toUtf8() );
1370 }
1371
1372 if ( !tz.isValid() )
1373 {
1374 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1375 return QVariant();
1376 }
1377
1378#else
1379 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1380#endif
1381 return QVariant::fromValue( tz );
1382}
1383
1384static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1385{
1386#if QT_FEATURE_timezone > 0
1387 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1388 if ( datetime.isValid() )
1389 {
1390 return QVariant::fromValue( datetime.timeZone() );
1391 }
1392 return QVariant();
1393#else
1394 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1395 return QVariant();
1396#endif
1397}
1398
1399static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1400{
1401#if QT_FEATURE_timezone > 0
1402 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1403 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1404 if ( datetime.isValid() && tz.isValid() )
1405 {
1406 datetime.setTimeZone( tz );
1407 return QVariant::fromValue( datetime );
1408 }
1409 return QVariant();
1410#else
1411 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1412 return QVariant();
1413#endif
1414}
1415
1416static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1417{
1418#if QT_FEATURE_timezone > 0
1419 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1420 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1421 if ( datetime.isValid() && tz.isValid() )
1422 {
1423 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1424 }
1425 return QVariant();
1426#else
1427 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1428 return QVariant();
1429#endif
1430}
1431
1432static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1433{
1434#if QT_FEATURE_timezone > 0
1435 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1436 if ( timeZone.isValid() )
1437 {
1438 return QString( timeZone.id() );
1439 }
1440 return QVariant();
1441#else
1442 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1443 return QVariant();
1444#endif
1445}
1446
1447static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1448{
1449 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1450 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1451 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1452 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1453 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1454 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1455 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1456
1457 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1458}
1459
1460static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1461{
1462 for ( const QVariant &value : values )
1463 {
1464 if ( QgsVariantUtils::isNull( value ) )
1465 continue;
1466 return value;
1467 }
1468 return QVariant();
1469}
1470
1471static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1472{
1473 const QVariant val1 = values.at( 0 );
1474 const QVariant val2 = values.at( 1 );
1475
1476 if ( val1 == val2 )
1477 return QVariant();
1478 else
1479 return val1;
1480}
1481
1482static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1483{
1484 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1485 return QVariant( str.toLower() );
1486}
1487static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1488{
1489 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1490 return QVariant( str.toUpper() );
1491}
1492static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1493{
1494 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1495 QStringList elems = str.split( ' ' );
1496 for ( int i = 0; i < elems.size(); i++ )
1497 {
1498 if ( elems[i].size() > 1 )
1499 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1500 }
1501 return QVariant( elems.join( ' '_L1 ) );
1502}
1503
1504static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1505{
1506 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1507 return QVariant( str.trimmed() );
1508}
1509
1510static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1511{
1512 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1513
1514 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1515
1516 const QRegularExpression re( u"^([%1]*)"_s.arg( QRegularExpression::escape( characters ) ) );
1517 str.replace( re, QString() );
1518 return QVariant( str );
1519}
1520
1521static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1522{
1523 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1524
1525 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1526
1527 const QRegularExpression re( u"([%1]*)$"_s.arg( QRegularExpression::escape( characters ) ) );
1528 str.replace( re, QString() );
1529 return QVariant( str );
1530}
1531
1532static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1533{
1534 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1535 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1536 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1537}
1538
1539static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1540{
1541 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1542 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1543 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1544}
1545
1546static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1547{
1548 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1549 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1550 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1551 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1552}
1553
1554static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1555{
1556 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1557 return QVariant( QgsStringUtils::soundex( string ) );
1558}
1559
1560static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1561{
1562 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1563 return QVariant( QString( character ) );
1564}
1565
1566static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1567{
1568 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1569
1570 if ( value.isEmpty() )
1571 {
1572 return QVariant();
1573 }
1574
1575 int res = value.at( 0 ).unicode();
1576 return QVariant( res );
1577}
1578
1579static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1580{
1581 if ( values.length() == 2 || values.length() == 3 )
1582 {
1583 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1584 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1585
1586 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1587
1588 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1589 }
1590
1591 return QVariant();
1592}
1593
1594static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1595{
1596 // two variants, one for geometry, one for string
1597
1598 //geometry variant
1599 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1600 if ( !geom.isNull() )
1601 {
1602 if ( geom.type() == Qgis::GeometryType::Line )
1603 return QVariant( geom.length() );
1604 else
1605 return QVariant();
1606 }
1607
1608 //otherwise fall back to string variant
1609 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1610 return QVariant( str.length() );
1611}
1612
1613static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1614{
1615 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1616
1617 if ( geom.type() != Qgis::GeometryType::Line )
1618 return QVariant();
1619
1620 double totalLength = 0;
1621 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1622 {
1624 {
1625 totalLength += line->length3D();
1626 }
1627 else
1628 {
1629 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1630 totalLength += segmentized->length3D();
1631 }
1632 }
1633
1634 return totalLength;
1635}
1636
1637
1638static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1639{
1640 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1641 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1642 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1643}
1644
1645static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1646{
1647 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1648 {
1649 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1650 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1651 QVector< QPair< QString, QString > > mapItems;
1652
1653 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1654 {
1655 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1656 }
1657
1658 // larger keys should be replaced first since they may contain whole smaller keys
1659 std::sort( mapItems.begin(), mapItems.end(), []( const QPair< QString, QString > &pair1, const QPair< QString, QString > &pair2 ) { return ( pair1.first.length() > pair2.first.length() ); } );
1660
1661 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1662 {
1663 str = str.replace( it->first, it->second );
1664 }
1665
1666 return QVariant( str );
1667 }
1668 else if ( values.count() == 3 )
1669 {
1670 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1671 QVariantList before;
1672 QVariantList after;
1673 bool isSingleReplacement = false;
1674
1675 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1676 {
1677 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1678 }
1679 else
1680 {
1681 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1682 }
1683
1684 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1685 {
1686 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1687 isSingleReplacement = true;
1688 }
1689 else
1690 {
1691 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1692 }
1693
1694 if ( !isSingleReplacement && before.length() != after.length() )
1695 {
1696 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1697 return QVariant();
1698 }
1699
1700 for ( int i = 0; i < before.length(); i++ )
1701 {
1702 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1703 }
1704
1705 return QVariant( str );
1706 }
1707 else
1708 {
1709 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1710 return QVariant();
1711 }
1712}
1713
1714static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1715{
1716 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1717 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1718 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1719
1720 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1721 if ( !re.isValid() )
1722 {
1723 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1724 return QVariant();
1725 }
1726 return QVariant( str.replace( re, after ) );
1727}
1728
1729static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1730{
1731 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1732 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1733
1734 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1735 if ( !re.isValid() )
1736 {
1737 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1738 return QVariant();
1739 }
1740 return QVariant( ( str.indexOf( re ) + 1 ) );
1741}
1742
1743static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1744{
1745 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1746 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1747 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1748
1749 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1750 if ( !re.isValid() )
1751 {
1752 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1753 return QVariant();
1754 }
1755
1756 QRegularExpressionMatch matches = re.match( str );
1757 if ( matches.hasMatch() )
1758 {
1759 QVariantList array;
1760 QStringList list = matches.capturedTexts();
1761
1762 // Skip the first string to only return captured groups
1763 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1764 {
1765 array += ( !( *it ).isEmpty() ) ? *it : empty;
1766 }
1767
1768 return QVariant( array );
1769 }
1770 else
1771 {
1772 return QVariant();
1773 }
1774}
1775
1776static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1777{
1778 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1779 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1780
1781 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1782 if ( !re.isValid() )
1783 {
1784 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1785 return QVariant();
1786 }
1787
1788 // extract substring
1789 QRegularExpressionMatch match = re.match( str );
1790 if ( match.hasMatch() )
1791 {
1792 // return first capture
1793 if ( match.lastCapturedIndex() > 0 )
1794 {
1795 // a capture group was present, so use that
1796 return QVariant( match.captured( 1 ) );
1797 }
1798 else
1799 {
1800 // no capture group, so using all match
1801 return QVariant( match.captured( 0 ) );
1802 }
1803 }
1804 else
1805 {
1806 return QVariant( "" );
1807 }
1808}
1809
1810static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1811{
1812 QString uuid = QUuid::createUuid().toString();
1813 if ( values.at( 0 ).toString().compare( u"WithoutBraces"_s, Qt::CaseInsensitive ) == 0 )
1814 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1815 else if ( values.at( 0 ).toString().compare( u"Id128"_s, Qt::CaseInsensitive ) == 0 )
1816 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1817 return uuid;
1818}
1819
1820static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1821{
1822 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1823 return QVariant();
1824
1825 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1826 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1827
1828 int len = 0;
1829 if ( values.at( 2 ).isValid() )
1830 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1831 else
1832 len = str.size();
1833
1834 if ( from < 0 )
1835 {
1836 from = str.size() + from;
1837 if ( from < 0 )
1838 {
1839 from = 0;
1840 }
1841 }
1842 else if ( from > 0 )
1843 {
1844 //account for the fact that substr() starts at 1
1845 from -= 1;
1846 }
1847
1848 if ( len < 0 )
1849 {
1850 len = str.size() + len - from;
1851 if ( len < 0 )
1852 {
1853 len = 0;
1854 }
1855 }
1856
1857 return QVariant( str.mid( from, len ) );
1858}
1859static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1860{
1861 FEAT_FROM_CONTEXT( context, f )
1862 return QVariant( f.id() );
1863}
1864
1865static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1866{
1867 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1868 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1869 bool foundLayer = false;
1870 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
1871 values.at( 0 ),
1872 context,
1873 parent,
1874 [parent, bandNb, geom]( QgsMapLayer *mapLayer ) {
1875 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1876 if ( !layer || !layer->dataProvider() )
1877 {
1878 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1879 return QVariant();
1880 }
1881
1882 if ( bandNb < 1 || bandNb > layer->bandCount() )
1883 {
1884 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1885 return QVariant();
1886 }
1887
1888 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1889 {
1890 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1891 return QVariant();
1892 }
1893
1894 QgsPointXY point = geom.asPoint();
1895 if ( geom.isMultipart() )
1896 {
1897 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1898 if ( multiPoint.count() == 1 )
1899 {
1900 point = multiPoint[0];
1901 }
1902 else
1903 {
1904 // if the geometry contains more than one part, return an undefined value
1905 return QVariant();
1906 }
1907 }
1908
1909 double value = layer->dataProvider()->sample( point, bandNb );
1910 return std::isnan( value ) ? QVariant() : value;
1911 },
1912 foundLayer
1913 );
1914
1915 if ( !foundLayer )
1916 {
1917 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1918 return QVariant();
1919 }
1920 else
1921 {
1922 return res;
1923 }
1924}
1925
1926static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1927{
1928 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1929 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1930
1931 bool foundLayer = false;
1932 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
1933 values.at( 0 ),
1934 context,
1935 parent,
1936 [parent, bandNb, value]( QgsMapLayer *mapLayer ) -> QVariant {
1937 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1938 if ( !layer || !layer->dataProvider() )
1939 {
1940 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1941 return QVariant();
1942 }
1943
1944 if ( bandNb < 1 || bandNb > layer->bandCount() )
1945 {
1946 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1947 return QVariant();
1948 }
1949
1950 if ( std::isnan( value ) )
1951 {
1952 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1953 return QVariant();
1954 }
1955
1956 if ( !layer->dataProvider()->attributeTable( bandNb ) )
1957 {
1958 return QVariant();
1959 }
1960
1961 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1962 if ( data.isEmpty() )
1963 {
1964 return QVariant();
1965 }
1966
1967 QVariantMap result;
1968 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1969 for ( int idx = 0; idx < static_cast<int>( fields.count() ) && idx < static_cast<int>( data.count() ); ++idx )
1970 {
1971 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1972 if ( field.isColor() || field.isRamp() )
1973 {
1974 continue;
1975 }
1976 result.insert( fields.at( idx ).name, data.at( idx ) );
1977 }
1978
1979 return result;
1980 },
1981 foundLayer
1982 );
1983
1984 if ( !foundLayer )
1985 {
1986 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1987 return QVariant();
1988 }
1989 else
1990 {
1991 return res;
1992 }
1993}
1994
1995static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1996{
1997 if ( !context )
1998 return QVariant();
1999
2000 return context->feature();
2001}
2002
2003static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2004{
2005 QgsFeature feature;
2006 QString attr;
2007 if ( values.size() == 1 )
2008 {
2009 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2010 feature = context->feature();
2011 }
2012 else if ( values.size() == 2 )
2013 {
2014 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2015 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2016 }
2017 else
2018 {
2019 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2020 return QVariant();
2021 }
2022
2023 return feature.attribute( attr );
2024}
2025
2026static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2027{
2028 QString table { R"html(
2029 <table>
2030 <thead>
2031 <tr><th>%1</th></tr>
2032 </thead>
2033 <tbody>
2034 <tr><td>%2</td></tr>
2035 </tbody>
2036 </table>)html" };
2037 QVariantMap dict;
2038 if ( values.size() == 1 )
2039 {
2040 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2041 }
2042 else
2043 {
2044 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2045 return QVariant();
2046 }
2047
2048 if ( dict.isEmpty() )
2049 {
2050 return QVariant();
2051 }
2052
2053 QStringList headers;
2054 QStringList cells;
2055
2056 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2057 {
2058 headers.push_back( it.key().toHtmlEscaped() );
2059 cells.push_back( it.value().toString().toHtmlEscaped() );
2060 }
2061
2062 return table.arg( headers.join( "</th><th>"_L1 ), cells.join( "</td><td>"_L1 ) );
2063}
2064
2065static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2066{
2067 QString table { R"html(
2068 <dl>
2069 %1
2070 </dl>)html" };
2071 QVariantMap dict;
2072 if ( values.size() == 1 )
2073 {
2074 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2075 }
2076 else
2077 {
2078 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2079 return QVariant();
2080 }
2081
2082 if ( dict.isEmpty() )
2083 {
2084 return QVariant();
2085 }
2086
2087 QString rows;
2088
2089 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2090 {
2091 rows.append( u"<dt>%1</dt><dd>%2</dd>"_s.arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2092 }
2093
2094 return table.arg( rows );
2095}
2096
2097static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2098{
2099 QVariant layer;
2100 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2101 {
2102 layer = context->variable( u"layer"_s );
2103 }
2104 else
2105 {
2106 //first node is layer id or name
2107 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2109 layer = node->eval( parent, context );
2111 }
2112
2113 QgsFeature feature;
2114 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2115 {
2116 feature = context->feature();
2117 }
2118 else
2119 {
2120 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2121 }
2122
2124 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2125 if ( strength == "hard"_L1 )
2126 {
2128 }
2129 else if ( strength == "soft"_L1 )
2130 {
2132 }
2133
2134 bool foundLayer = false;
2135 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2136 layer,
2137 context,
2138 parent,
2139 [parent, feature, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2140 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2141 if ( !layer )
2142 {
2143 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2144 return QVariant();
2145 }
2146
2147 const QgsFields fields = layer->fields();
2148 bool valid = true;
2149 for ( int i = 0; i < fields.size(); i++ )
2150 {
2151 QStringList errors;
2152 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2153 if ( !valid )
2154 {
2155 break;
2156 }
2157 }
2158
2159 return valid;
2160 },
2161 foundLayer
2162 );
2163
2164 if ( !foundLayer )
2165 {
2166 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2167 return QVariant();
2168 }
2169
2170 return res;
2171}
2172
2173static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2174{
2175 QVariant layer;
2176 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2177 {
2178 layer = context->variable( u"layer"_s );
2179 }
2180 else
2181 {
2182 //first node is layer id or name
2183 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2185 layer = node->eval( parent, context );
2187 }
2188
2189 QgsFeature feature;
2190 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2191 {
2192 feature = context->feature();
2193 }
2194 else
2195 {
2196 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2197 }
2198
2200 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2201 if ( strength == "hard"_L1 )
2202 {
2204 }
2205 else if ( strength == "soft"_L1 )
2206 {
2208 }
2209
2210 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2211
2212 bool foundLayer = false;
2213 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2214 layer,
2215 context,
2216 parent,
2217 [parent, feature, attributeName, constraintStrength]( QgsMapLayer *mapLayer ) -> QVariant {
2218 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2219 if ( !layer )
2220 {
2221 return QVariant();
2222 }
2223
2224 const int fieldIndex = layer->fields().indexFromName( attributeName );
2225 if ( fieldIndex == -1 )
2226 {
2227 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2228 return QVariant();
2229 }
2230
2231 QStringList errors;
2232 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2233 return valid;
2234 },
2235 foundLayer
2236 );
2237
2238 if ( !foundLayer )
2239 {
2240 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2241 return QVariant();
2242 }
2243
2244 return res;
2245}
2246
2247static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2248{
2249 QgsFeature feature;
2250 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2251 {
2252 feature = context->feature();
2253 }
2254 else
2255 {
2256 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2257 }
2258
2259 const QgsFields fields = feature.fields();
2260 QVariantMap result;
2261 for ( int i = 0; i < fields.count(); ++i )
2262 {
2263 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2264 }
2265 return result;
2266}
2267
2268static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2269{
2270 QgsVectorLayer *layer = nullptr;
2271 QgsFeature feature;
2272
2273 // TODO this expression function is NOT thread safe
2275 if ( values.isEmpty() )
2276 {
2277 feature = context->feature();
2278 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2279 }
2280 else if ( values.size() == 1 )
2281 {
2282 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2283 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2284 }
2285 else if ( values.size() == 2 )
2286 {
2287 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2288 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2289 }
2290 else
2291 {
2292 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2293 return QVariant();
2294 }
2296
2297 if ( !layer )
2298 {
2299 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2300 return QVariant();
2301 }
2302
2303 if ( !feature.isValid() )
2304 {
2305 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2306 return QVariant();
2307 }
2308
2309 const QgsFields fields = feature.fields();
2310 QVariantMap result;
2311 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2312 {
2313 const QString fieldName { fields.at( fieldIndex ).name() };
2314 const QVariant attributeVal = feature.attribute( fieldIndex );
2315 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer->id(), fieldName, attributeVal.toString() );
2316 if ( context && context->hasCachedValue( cacheValueKey ) )
2317 {
2318 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2319 }
2320 else
2321 {
2322 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2324 QVariant cache;
2325 if ( context )
2326 {
2327 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer->id(), fieldName );
2328
2329 if ( !context->hasCachedValue( cacheKey ) )
2330 {
2331 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2332 context->setCachedValue( cacheKey, cache );
2333 }
2334 else
2335 {
2336 cache = context->cachedValue( cacheKey );
2337 }
2338 }
2339 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2340
2341 result.insert( fields.at( fieldIndex ).name(), value );
2342
2343 if ( context )
2344 {
2345 context->setCachedValue( cacheValueKey, value );
2346 }
2347 }
2348 }
2349 return result;
2350}
2351
2352static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2353{
2354 QgsVectorLayer *layer = nullptr;
2355 QgsFeature feature;
2356 bool evaluate = true;
2357
2358 // TODO this expression function is NOT thread safe
2360 if ( values.isEmpty() )
2361 {
2362 feature = context->feature();
2363 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2364 }
2365 else if ( values.size() == 1 )
2366 {
2367 layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
2368 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2369 }
2370 else if ( values.size() == 2 )
2371 {
2372 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2373 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2374 }
2375 else if ( values.size() == 3 )
2376 {
2377 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2378 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2379 evaluate = values.value( 2 ).toBool();
2380 }
2381 else
2382 {
2383 if ( isMaptip )
2384 {
2385 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2386 }
2387 else
2388 {
2389 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2390 }
2391 return QVariant();
2392 }
2393
2394 if ( !layer )
2395 {
2396 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2397 return QVariant();
2398 }
2400
2401 if ( !feature.isValid() )
2402 {
2403 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2404 return QVariant();
2405 }
2406
2407 if ( !evaluate )
2408 {
2409 if ( isMaptip )
2410 {
2411 return layer->mapTipTemplate();
2412 }
2413 else
2414 {
2415 return layer->displayExpression();
2416 }
2417 }
2418
2419 QgsExpressionContext subContext( *context );
2420 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2421 subContext.setFeature( feature );
2422
2423 if ( isMaptip )
2424 {
2425 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2426 }
2427 else
2428 {
2429 QgsExpression exp( layer->displayExpression() );
2430 exp.prepare( &subContext );
2431 return exp.evaluate( &subContext ).toString();
2432 }
2433}
2434
2435static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2436{
2437 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2438}
2439
2440static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2441{
2442 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2443}
2444
2445static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2446{
2447 QgsFeature feature;
2448 QVariant layer;
2449 if ( values.isEmpty() )
2450 {
2451 feature = context->feature();
2452 layer = context->variable( u"layer"_s );
2453 }
2454 else if ( values.size() == 1 )
2455 {
2456 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2457 layer = context->variable( u"layer"_s );
2458 }
2459 else if ( values.size() == 2 )
2460 {
2461 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2462 layer = values.at( 0 );
2463 }
2464 else
2465 {
2466 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2467 return QVariant();
2468 }
2469
2470 bool foundLayer = false;
2471 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2472 layer,
2473 context,
2474 parent,
2475 [feature]( QgsMapLayer *mapLayer ) -> QVariant {
2476 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2477 if ( !layer || !feature.isValid() )
2478 {
2479 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2480 }
2481
2482 return layer->selectedFeatureIds().contains( feature.id() );
2483 },
2484 foundLayer
2485 );
2486 if ( !foundLayer )
2487 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2488 else
2489 return res;
2490}
2491
2492static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2493{
2494 QVariant layer;
2495
2496 if ( values.isEmpty() )
2497 layer = context->variable( u"layer"_s );
2498 else if ( values.count() == 1 )
2499 layer = values.at( 0 );
2500 else
2501 {
2502 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2503 return QVariant();
2504 }
2505
2506 bool foundLayer = false;
2507 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
2508 layer,
2509 context,
2510 parent,
2511 []( QgsMapLayer *mapLayer ) -> QVariant {
2512 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2513 if ( !layer )
2514 {
2515 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2516 }
2517
2518 return layer->selectedFeatureCount();
2519 },
2520 foundLayer
2521 );
2522 if ( !foundLayer )
2523 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2524 else
2525 return res;
2526}
2527
2528static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2529{
2530 static QMap<QString, qlonglong> counterCache;
2531 QVariant functionResult;
2532
2533 auto fetchAndIncrementFunc = [values, parent, &functionResult]( QgsMapLayer *mapLayer, const QString &databaseArgument ) {
2534 QString database;
2535
2536 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2537
2538 if ( layer )
2539 {
2540 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2541 database = decodedUri.value( u"path"_s ).toString();
2542 if ( database.isEmpty() )
2543 {
2544 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2545 }
2546 }
2547 else
2548 {
2549 database = databaseArgument;
2550 }
2551
2552 const QString table = values.at( 1 ).toString();
2553 const QString idColumn = values.at( 2 ).toString();
2554 const QString filterAttribute = values.at( 3 ).toString();
2555 const QVariant filterValue = values.at( 4 ).toString();
2556 const QVariantMap defaultValues = values.at( 5 ).toMap();
2557
2558 // read from database
2560 sqlite3_statement_unique_ptr sqliteStatement;
2561
2562 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2563 {
2564 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2565 functionResult = QVariant();
2566 return;
2567 }
2568
2569 QString errorMessage;
2570 QString currentValSql;
2571
2572 qlonglong nextId = 0;
2573 bool cachedMode = false;
2574 bool valueRetrieved = false;
2575
2576 QString cacheString = u"%1:%2:%3:%4:%5"_s.arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2577
2578 // Running in transaction mode, check for cached value first
2579 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2580 {
2581 cachedMode = true;
2582
2583 auto cachedCounter = counterCache.find( cacheString );
2584
2585 if ( cachedCounter != counterCache.end() )
2586 {
2587 qlonglong &cachedValue = cachedCounter.value();
2588 nextId = cachedValue;
2589 nextId += 1;
2590 cachedValue = nextId;
2591 valueRetrieved = true;
2592 }
2593 }
2594
2595 // Either not in cached mode or no cached value found, obtain from DB
2596 if ( !cachedMode || !valueRetrieved )
2597 {
2598 int result = SQLITE_ERROR;
2599
2600 currentValSql = u"SELECT %1 FROM %2"_s.arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2601 if ( !filterAttribute.isNull() )
2602 {
2603 currentValSql += u" WHERE %1 = %2"_s.arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2604 }
2605
2606 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2607
2608 if ( result == SQLITE_OK )
2609 {
2610 nextId = 0;
2611 if ( sqliteStatement.step() == SQLITE_ROW )
2612 {
2613 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2614 }
2615
2616 // If in cached mode: add value to cache and connect to transaction
2617 if ( cachedMode && result == SQLITE_OK )
2618 {
2619 counterCache.insert( cacheString, nextId );
2620
2621 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]() { counterCache.remove( cacheString ); } );
2622 }
2623 valueRetrieved = true;
2624 }
2625 }
2626
2627 if ( valueRetrieved )
2628 {
2629 QString upsertSql;
2630 upsertSql = u"INSERT OR REPLACE INTO %1"_s.arg( QgsSqliteUtils::quotedIdentifier( table ) );
2631 QStringList cols;
2632 QStringList vals;
2633 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2634 vals << QgsSqliteUtils::quotedValue( nextId );
2635
2636 if ( !filterAttribute.isNull() )
2637 {
2638 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2639 vals << QgsSqliteUtils::quotedValue( filterValue );
2640 }
2641
2642 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2643 {
2644 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2645 vals << iter.value().toString();
2646 }
2647
2648 upsertSql += " ("_L1 + cols.join( ',' ) + ')';
2649 upsertSql += " VALUES "_L1;
2650 upsertSql += '(' + vals.join( ',' ) + ')';
2651
2652 int result = SQLITE_ERROR;
2653 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2654 {
2655 QgsTransaction *transaction = layer->dataProvider()->transaction();
2656 if ( transaction->executeSql( upsertSql, errorMessage ) )
2657 {
2658 result = SQLITE_OK;
2659 }
2660 }
2661 else
2662 {
2663 result = sqliteDb.exec( upsertSql, errorMessage );
2664 }
2665 if ( result == SQLITE_OK )
2666 {
2667 functionResult = QVariant( nextId );
2668 return;
2669 }
2670 else
2671 {
2672 parent->setEvalErrorString( u"Could not increment value: SQLite error: \"%1\" (%2)."_s.arg( errorMessage, QString::number( result ) ) );
2673 functionResult = QVariant();
2674 return;
2675 }
2676 }
2677
2678 functionResult = QVariant();
2679 };
2680
2681 bool foundLayer = false;
2682 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer *layer ) { fetchAndIncrementFunc( layer, QString() ); }, foundLayer );
2683 if ( !foundLayer )
2684 {
2685 const QString databasePath = values.at( 0 ).toString();
2686 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath] { fetchAndIncrementFunc( nullptr, databasePath ); } );
2687 }
2688
2689 return functionResult;
2690}
2691
2692static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2693{
2694 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2695 if ( !crs.isValid() )
2696 return QVariant();
2697 return crs.authid();
2698}
2699
2700static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2701{
2702 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2703 QgsCoordinateReferenceSystem crs( definition );
2704
2705 if ( !crs.isValid() )
2706 {
2707 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2708 }
2709
2710 return QVariant::fromValue( crs );
2711}
2712
2713static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2714{
2715 QString concat;
2716 for ( const QVariant &value : values )
2717 {
2718 if ( !QgsVariantUtils::isNull( value ) )
2719 concat += QgsExpressionUtils::getStringValue( value, parent );
2720 }
2721 return concat;
2722}
2723
2724static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2725{
2726 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2727 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2728}
2729
2730static QVariant fcnUnaccent( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction *node )
2731{
2732 Q_UNUSED( context )
2733 Q_UNUSED( node )
2734
2735 if ( values.isEmpty() || values[0].isNull() )
2736 return QVariant();
2737
2738 return QgsStringUtils::unaccent( values[0].toString() );
2739}
2740
2741
2742static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2743{
2744 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2745 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2746 return string.right( pos );
2747}
2748
2749static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2750{
2751 if ( values.length() < 2 || values.length() > 3 )
2752 return QVariant();
2753
2754 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2755 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2756
2757 bool overlapping = false;
2758 if ( values.length() == 3 )
2759 {
2760 overlapping = values.at( 2 ).toBool();
2761 }
2762
2763 if ( substring.isEmpty() )
2764 return QVariant( 0 );
2765
2766 int count = 0;
2767 if ( overlapping )
2768 {
2769 count = input.count( substring );
2770 }
2771 else
2772 {
2773 int pos = 0;
2774 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2775 {
2776 count++;
2777 pos += substring.length();
2778 }
2779 }
2780
2781 return QVariant( count );
2782}
2783
2784static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2785{
2786 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2787 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2788 return string.left( pos );
2789}
2790
2791static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2792{
2793 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2794 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2795 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2796 return string.leftJustified( length, fill.at( 0 ), true );
2797}
2798
2799static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2800{
2801 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2802 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2803 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2804 return string.rightJustified( length, fill.at( 0 ), true );
2805}
2806
2807static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2808{
2809 if ( values.size() < 1 )
2810 {
2811 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2812 return QVariant();
2813 }
2814
2815 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2816 for ( int n = 1; n < values.length(); n++ )
2817 {
2818 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2819 }
2820 return string;
2821}
2822
2823
2824static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2825{
2826 return QVariant( QDateTime::currentDateTime() );
2827}
2828
2829static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2830{
2831 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2832 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2833 if ( format.isEmpty() && !language.isEmpty() )
2834 {
2835 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2836 return QVariant( QDate() );
2837 }
2838
2839 if ( format.isEmpty() && language.isEmpty() )
2840 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2841
2842 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2843 QLocale locale = QLocale();
2844 if ( !language.isEmpty() )
2845 {
2846 locale = QLocale( language );
2847 }
2848
2849 QDate date = locale.toDate( datestring, format );
2850 if ( !date.isValid() )
2851 {
2852 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2853 date = QDate();
2854 }
2855 return QVariant( date );
2856}
2857
2858static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2859{
2860 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2861 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2862 if ( format.isEmpty() && !language.isEmpty() )
2863 {
2864 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2865 return QVariant( QTime() );
2866 }
2867
2868 if ( format.isEmpty() && language.isEmpty() )
2869 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2870
2871 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2872 QLocale locale = QLocale();
2873 if ( !language.isEmpty() )
2874 {
2875 locale = QLocale( language );
2876 }
2877
2878 QTime time = locale.toTime( timestring, format );
2879 if ( !time.isValid() )
2880 {
2881 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2882 time = QTime();
2883 }
2884 return QVariant( time );
2885}
2886
2887static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2888{
2889 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2890}
2891
2892/*
2893 * DMS functions
2894 */
2895
2896static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2897{
2898 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2899 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2900 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2901
2902 QString formatString;
2903 if ( values.count() > 3 )
2904 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2905
2907 if ( formatString.compare( "suffix"_L1, Qt::CaseInsensitive ) == 0 )
2908 {
2910 }
2911 else if ( formatString.compare( "aligned"_L1, Qt::CaseInsensitive ) == 0 )
2912 {
2914 }
2915 else if ( !formatString.isEmpty() )
2916 {
2917 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2918 return QVariant();
2919 }
2920
2921 if ( axis.compare( 'x'_L1, Qt::CaseInsensitive ) == 0 )
2922 {
2923 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2924 }
2925 else if ( axis.compare( 'y'_L1, Qt::CaseInsensitive ) == 0 )
2926 {
2927 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2928 }
2929 else
2930 {
2931 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2932 return QVariant();
2933 }
2934}
2935
2936static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2937{
2939 return floatToDegreeFormat( format, values, context, parent, node );
2940}
2941
2942static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2943{
2944 double value = 0.0;
2945 bool ok = false;
2946 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2947
2948 return ok ? QVariant( value ) : QVariant();
2949}
2950
2951static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2952{
2954 return floatToDegreeFormat( format, values, context, parent, node );
2955}
2956
2957static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2958{
2959 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2960 return static_cast< int >( decimalDegrees );
2961}
2962
2963static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2964{
2965 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2966 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2967 return static_cast< int >( remainder * 60 );
2968}
2969
2970static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2971{
2972 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2973 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2974 const double remainderInMinutes = remainder * 60;
2975 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
2976 // do not truncate to int, this function returns decimal seconds!
2977 return remainderSecondsFraction * 60;
2978}
2979
2980static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2981{
2982 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2983 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2984 qint64 seconds = d2.secsTo( d1 );
2985 return QVariant::fromValue( QgsInterval( seconds ) );
2986}
2987
2988static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2989{
2990 if ( !values.at( 0 ).canConvert<QDate>() )
2991 return QVariant();
2992
2993 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2994 if ( !date.isValid() )
2995 return QVariant();
2996
2997 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2998 // (to match PostgreSQL behavior)
2999 return date.dayOfWeek() % 7;
3000}
3001
3002static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3003{
3004 QVariant value = values.at( 0 );
3005 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3006 if ( inter.isValid() )
3007 {
3008 return QVariant( inter.days() );
3009 }
3010 else
3011 {
3012 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3013 return QVariant( d1.date().day() );
3014 }
3015}
3016
3017static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3018{
3019 QVariant value = values.at( 0 );
3020 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3021 if ( inter.isValid() )
3022 {
3023 return QVariant( inter.years() );
3024 }
3025 else
3026 {
3027 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3028 return QVariant( d1.date().year() );
3029 }
3030}
3031
3032static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3033{
3034 QVariant value = values.at( 0 );
3035 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3036 if ( inter.isValid() )
3037 {
3038 return QVariant( inter.months() );
3039 }
3040 else
3041 {
3042 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3043 return QVariant( d1.date().month() );
3044 }
3045}
3046
3047static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3048{
3049 QVariant value = values.at( 0 );
3050 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3051 if ( inter.isValid() )
3052 {
3053 return QVariant( inter.weeks() );
3054 }
3055 else
3056 {
3057 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3058 return QVariant( d1.date().weekNumber() );
3059 }
3060}
3061
3062static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3063{
3064 QVariant value = values.at( 0 );
3065 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3066 if ( inter.isValid() )
3067 {
3068 return QVariant( inter.hours() );
3069 }
3070 else
3071 {
3072 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3073 return QVariant( t1.hour() );
3074 }
3075}
3076
3077static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3078{
3079 QVariant value = values.at( 0 );
3080 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3081 if ( inter.isValid() )
3082 {
3083 return QVariant( inter.minutes() );
3084 }
3085 else
3086 {
3087 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3088 return QVariant( t1.minute() );
3089 }
3090}
3091
3092static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3093{
3094 QVariant value = values.at( 0 );
3095 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3096 if ( inter.isValid() )
3097 {
3098 return QVariant( inter.seconds() );
3099 }
3100 else
3101 {
3102 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3103 return QVariant( t1.second() );
3104 }
3105}
3106
3107static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3108{
3109 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3110 if ( dt.isValid() )
3111 {
3112 return QVariant( dt.toMSecsSinceEpoch() );
3113 }
3114 else
3115 {
3116 return QVariant();
3117 }
3118}
3119
3120static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3121{
3122 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3123 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3124 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3125}
3126
3127static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3128{
3129 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3130 if ( parent->hasEvalError() )
3131 {
3132 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif"_L1 ) );
3133 return QVariant();
3134 }
3135 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3136 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3137}
3138
3139static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3141 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3142 if ( parent->hasEvalError() )
3143 {
3144 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "exif_geotag"_L1 ) );
3145 return QVariant();
3146 }
3147 bool ok;
3148 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3149}
3150
3151double qDateTimeToDecimalYear( const QDateTime &dateTime )
3152{
3153 if ( !dateTime.isValid() )
3154 {
3155 return 0.0;
3156 }
3157
3158 const int year = dateTime.date().year();
3159 const QDateTime startOfYear( QDate( year, 1, 1 ), QTime( 0, 0, 0 ) );
3160 const QDateTime startOfNextYear( QDate( year + 1, 1, 1 ), QTime( 0, 0, 0 ) );
3161 const qint64 secondsFromStartOfYear = startOfYear.secsTo( dateTime );
3162 const qint64 totalSecondsInYear = startOfYear.secsTo( startOfNextYear );
3163 return static_cast<double>( year ) + ( static_cast<double>( secondsFromStartOfYear ) / static_cast< double >( totalSecondsInYear ) );
3164}
3165
3166static QVariant fcnMagneticDeclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3167{
3168 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3169 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3170 if ( parent->hasEvalError() )
3171 {
3172 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination"_L1 ) );
3173 return QVariant();
3174 }
3175 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3176 if ( parent->hasEvalError() )
3177 {
3178 return QVariant();
3179 }
3180 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3181 if ( parent->hasEvalError() )
3182 {
3183 return QVariant();
3184 }
3185 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3186 if ( parent->hasEvalError() )
3187 {
3188 return QVariant();
3189 }
3190 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3191
3192 const QgsMagneticModel model( name, filePath );
3193 try
3194 {
3195 double declination = 0;
3196 if ( model.declination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, declination ) )
3197 {
3198 return declination;
3199 }
3200 else
3201 {
3202 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( model.error() ) );
3203 }
3204 }
3205 catch ( QgsNotSupportedException &e )
3206 {
3207 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination: %1" ).arg( e.what() ) );
3208 }
3209 return QVariant();
3210}
3211
3212static QVariant fcnMagneticInclination( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3213{
3214 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3215 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3216 if ( parent->hasEvalError() )
3217 {
3218 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination"_L1 ) );
3219 return QVariant();
3220 }
3221 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3222 if ( parent->hasEvalError() )
3223 {
3224 return QVariant();
3225 }
3226 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3227 if ( parent->hasEvalError() )
3228 {
3229 return QVariant();
3230 }
3231 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3232 if ( parent->hasEvalError() )
3233 {
3234 return QVariant();
3235 }
3236 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3237
3238 const QgsMagneticModel model( name, filePath );
3239 try
3240 {
3241 double inclination = 0;
3242 if ( model.inclination( qDateTimeToDecimalYear( dt ), latitude, longitude, height, inclination ) )
3243 {
3244 return inclination;
3245 }
3246 else
3247 {
3248 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( model.error() ) );
3249 }
3250 }
3251 catch ( QgsNotSupportedException &e )
3252 {
3253 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination: %1" ).arg( e.what() ) );
3254 }
3255 return QVariant();
3256}
3257
3258static QVariant fcnMagneticDeclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3259{
3260 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3261 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3262 if ( parent->hasEvalError() )
3263 {
3264 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_declination_rate_of_change"_L1 ) );
3265 return QVariant();
3266 }
3267 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3268 if ( parent->hasEvalError() )
3269 {
3270 return QVariant();
3271 }
3272 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3273 if ( parent->hasEvalError() )
3274 {
3275 return QVariant();
3276 }
3277 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3278 if ( parent->hasEvalError() )
3279 {
3280 return QVariant();
3281 }
3282 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3283
3284 const QgsMagneticModel model( name, filePath );
3285 try
3286 {
3287 double declination = 0;
3288 double Bx = 0;
3289 double By = 0;
3290 double Bz = 0;
3291 double Bxt = 0;
3292 double Byt = 0;
3293 double Bzt = 0;
3294
3295 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3296 {
3297 double H = 0;
3298 double F = 0;
3299 double D = 0;
3300 double I = 0;
3301 double Ht = 0;
3302 double Ft = 0;
3303 double Dt = 0;
3304 double It = 0;
3305 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3306 {
3307 return Dt;
3308 }
3309 else
3310 {
3311 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change" ) );
3312 }
3313 return declination;
3314 }
3315 else
3316 {
3317 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( model.error() ) );
3318 }
3319 }
3320 catch ( QgsNotSupportedException &e )
3321 {
3322 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic declination rate of change: %1" ).arg( e.what() ) );
3323 }
3324 return QVariant();
3325}
3326
3327static QVariant fcnMagneticInclinationRateOfChange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3328{
3329 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
3330 const QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
3331 if ( parent->hasEvalError() )
3332 {
3333 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a valid date" ).arg( "magnetic_inclination_rate_of_change"_L1 ) );
3334 return QVariant();
3335 }
3336 const double latitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3337 if ( parent->hasEvalError() )
3338 {
3339 return QVariant();
3340 }
3341 const double longitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3342 if ( parent->hasEvalError() )
3343 {
3344 return QVariant();
3345 }
3346 const double height = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3347 if ( parent->hasEvalError() )
3348 {
3349 return QVariant();
3350 }
3351 const QString filePath = QgsExpressionUtils::getFilePathValue( values.at( 5 ), context, parent );
3352
3353 const QgsMagneticModel model( name, filePath );
3354 try
3355 {
3356 double declination = 0;
3357 double Bx = 0;
3358 double By = 0;
3359 double Bz = 0;
3360 double Bxt = 0;
3361 double Byt = 0;
3362 double Bzt = 0;
3363
3364 if ( model.getComponentsWithTimeDerivatives( qDateTimeToDecimalYear( dt ), latitude, longitude, height, Bx, By, Bz, Bxt, Byt, Bzt ) )
3365 {
3366 double H = 0;
3367 double F = 0;
3368 double D = 0;
3369 double I = 0;
3370 double Ht = 0;
3371 double Ft = 0;
3372 double Dt = 0;
3373 double It = 0;
3374 if ( QgsMagneticModel::fieldComponentsWithTimeDerivatives( Bx, By, Bz, Bxt, Byt, Bzt, H, F, D, I, Ht, Ft, Dt, It ) )
3375 {
3376 return It;
3377 }
3378 else
3379 {
3380 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change" ) );
3381 }
3382 return declination;
3383 }
3384 else
3386 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( model.error() ) );
3387 }
3388 }
3389 catch ( QgsNotSupportedException &e )
3390 {
3391 parent->setEvalErrorString( QObject::tr( "Cannot evaluate magnetic inclination rate of change: %1" ).arg( e.what() ) );
3392 }
3393 return QVariant();
3394}
3395
3396#define ENSURE_GEOM_TYPE( f, g, geomtype ) \
3397 if ( !( f ).hasGeometry() ) \
3398 return QVariant(); \
3399 QgsGeometry g = ( f ).geometry(); \
3400 if ( ( g ).type() != ( geomtype ) ) \
3401 return QVariant();
3402
3403static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3404{
3405 FEAT_FROM_CONTEXT( context, f )
3407 if ( g.isMultipart() )
3408 {
3409 return g.asMultiPoint().at( 0 ).x();
3410 }
3411 else
3412 {
3413 return g.asPoint().x();
3414 }
3415}
3416
3417static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3418{
3419 FEAT_FROM_CONTEXT( context, f )
3421 if ( g.isMultipart() )
3422 {
3423 return g.asMultiPoint().at( 0 ).y();
3424 }
3425 else
3426 {
3427 return g.asPoint().y();
3428 }
3429}
3430
3431static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3432{
3433 FEAT_FROM_CONTEXT( context, f )
3435
3436 if ( g.isEmpty() )
3437 return QVariant();
3438
3439 const QgsAbstractGeometry *abGeom = g.constGet();
3440
3441 if ( g.isEmpty() || !abGeom->is3D() )
3442 return QVariant();
3443
3444 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3445 {
3446 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3447 if ( point )
3448 return point->z();
3449 }
3450 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3451 {
3452 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3453 {
3454 if ( collection->numGeometries() > 0 )
3455 {
3456 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3457 return point->z();
3458 }
3459 }
3460 }
3461
3462 return QVariant();
3463}
3464
3465static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3466{
3467 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3468 if ( geom.isNull() )
3469 return QVariant();
3470
3471 bool isValid = geom.isGeosValid();
3472
3473 return QVariant( isValid );
3474}
3475
3476static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3477{
3478 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3479 if ( geom.isNull() )
3480 return QVariant();
3481
3482 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3483#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
3485#else
3487#endif
3488 if ( methodString.compare( "linework"_L1, Qt::CaseInsensitive ) == 0 )
3490 else if ( methodString.compare( "structure"_L1, Qt::CaseInsensitive ) == 0 )
3492
3493 const bool keepCollapsed = values.value( 2 ).toBool();
3494
3495 QgsGeometry valid;
3496 try
3497 {
3498 valid = geom.makeValid( method, keepCollapsed );
3499 }
3500 catch ( QgsNotSupportedException & )
3501 {
3502 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3503 return QVariant();
3504 }
3505
3506 return QVariant::fromValue( valid );
3507}
3508
3509static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3510{
3511 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3512 if ( geom.isNull() )
3513 return QVariant();
3514
3515 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3516 QVariantList array;
3517 for ( int i = 0; i < multiGeom.size(); ++i )
3518 {
3519 array += QVariant::fromValue( multiGeom.at( i ) );
3520 }
3521
3522 return array;
3523}
3524
3525static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3526{
3527 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3528 if ( geom.isNull() )
3529 return QVariant();
3530
3531 //if single point, return the point's x coordinate
3532 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3533 {
3534 return geom.asPoint().x();
3535 }
3536
3537 //otherwise return centroid x
3538 QgsGeometry centroid = geom.centroid();
3539 QVariant result( centroid.asPoint().x() );
3540 return result;
3541}
3542
3543static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3544{
3545 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3546 if ( geom.isNull() )
3547 return QVariant();
3548
3549 //if single point, return the point's y coordinate
3550 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3551 {
3552 return geom.asPoint().y();
3553 }
3554
3555 //otherwise return centroid y
3556 QgsGeometry centroid = geom.centroid();
3557 QVariant result( centroid.asPoint().y() );
3558 return result;
3559}
3560
3561static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3562{
3563 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3564 if ( geom.isNull() )
3565 return QVariant(); //or 0?
3566
3567 if ( !geom.constGet()->is3D() )
3568 return QVariant();
3569
3570 //if single point, return the point's z coordinate
3571 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3572 {
3574 if ( point )
3575 return point->z();
3576 }
3577 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3578 {
3580 {
3581 if ( collection->numGeometries() == 1 )
3582 {
3583 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3584 return point->z();
3585 }
3586 }
3587 }
3588
3589 return QVariant();
3590}
3591
3592static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3593{
3594 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3595 if ( geom.isNull() )
3596 return QVariant(); //or 0?
3597
3598 if ( !geom.constGet()->isMeasure() )
3599 return QVariant();
3600
3601 //if single point, return the point's m value
3602 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3603 {
3605 if ( point )
3606 return point->m();
3607 }
3608 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3609 {
3611 {
3612 if ( collection->numGeometries() == 1 )
3613 {
3614 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3615 return point->m();
3616 }
3617 }
3618 }
3619
3620 return QVariant();
3621}
3622
3623static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3624{
3625 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3626
3627 if ( geom.isNull() )
3628 return QVariant();
3629
3630 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3631
3632 if ( idx < 0 )
3633 {
3634 //negative idx
3635 int count = geom.constGet()->nCoordinates();
3636 idx = count + idx;
3637 }
3638 else
3639 {
3640 //positive idx is 1 based
3641 idx -= 1;
3642 }
3643
3644 QgsVertexId vId;
3645 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3646 {
3647 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3648 return QVariant();
3649 }
3650
3651 QgsPoint point = geom.constGet()->vertexAt( vId );
3652 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3653}
3654
3655static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3656{
3657 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3658
3659 if ( geom.isNull() )
3660 return QVariant();
3661
3662 QgsVertexId vId;
3663 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3664 {
3665 return QVariant();
3666 }
3667
3668 QgsPoint point = geom.constGet()->vertexAt( vId );
3669 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3670}
3671
3672static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3673{
3674 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3675
3676 if ( geom.isNull() )
3677 return QVariant();
3678
3679 QgsVertexId vId;
3680 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3681 {
3682 return QVariant();
3683 }
3684
3685 QgsPoint point = geom.constGet()->vertexAt( vId );
3686 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3687}
3688
3689static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3690{
3691 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3692
3693 if ( geom.isNull() )
3694 return QVariant();
3695
3696 bool ignoreClosing = false;
3697 if ( values.length() > 1 )
3698 {
3699 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3700 }
3701
3702 QgsMultiPoint *mp = new QgsMultiPoint();
3703
3704 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3705 for ( const QgsRingSequence &part : sequence )
3706 {
3707 for ( const QgsPointSequence &ring : part )
3708 {
3709 bool skipLast = false;
3710 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3711 {
3712 skipLast = true;
3713 }
3714
3715 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++i )
3716 {
3717 mp->addGeometry( ring.at( i ).clone() );
3718 }
3719 }
3720 }
3721
3722 return QVariant::fromValue( QgsGeometry( mp ) );
3723}
3724
3725static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3726{
3727 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3728
3729 if ( geom.isNull() )
3730 return QVariant();
3731
3732 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3733
3734 //OK, now we have a complete list of segmentized lines from the geometry
3736 for ( QgsLineString *line : linesToProcess )
3737 {
3738 for ( int i = 0; i < line->numPoints() - 1; ++i )
3739 {
3741 segment->setPoints( QgsPointSequence() << line->pointN( i ) << line->pointN( i + 1 ) );
3742 ml->addGeometry( segment );
3743 }
3744 delete line;
3745 }
3746
3747 return QVariant::fromValue( QgsGeometry( ml ) );
3748}
3749
3750static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3751{
3752 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3753
3754 if ( geom.isNull() )
3755 return QVariant();
3756
3758 if ( !curvePolygon && geom.isMultipart() )
3759 {
3761 {
3762 if ( collection->numGeometries() == 1 )
3763 {
3764 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3765 }
3766 }
3767 }
3768
3769 if ( !curvePolygon )
3770 return QVariant();
3771
3772 //idx is 1 based
3773 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3774
3775 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3776 return QVariant();
3777
3778 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3779 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3780 return result;
3781}
3782
3783static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3784{
3785 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3786
3787 if ( geom.isNull() )
3788 return QVariant();
3789
3791 if ( !collection )
3792 return QVariant();
3793
3794 //idx is 1 based
3795 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3796
3797 if ( idx < 0 || idx >= collection->numGeometries() )
3798 return QVariant();
3799
3800 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3801 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3802 return result;
3803}
3804
3805static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3806{
3807 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3808
3809 if ( geom.isNull() )
3810 return QVariant();
3811
3812 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3813 if ( !boundary )
3814 return QVariant();
3815
3816 return QVariant::fromValue( QgsGeometry( boundary ) );
3817}
3818
3819static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3820{
3821 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3822
3823 if ( geom.isNull() )
3824 return QVariant();
3825
3826 QgsGeometry merged = geom.mergeLines();
3827 if ( merged.isNull() )
3828 return QVariant();
3829
3830 return QVariant::fromValue( merged );
3831}
3832
3833static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3834{
3835 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3836 if ( geom.isNull() )
3837 return QVariant();
3838
3839 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3840 if ( geom2.isNull() )
3841 return QVariant();
3842
3843 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3844 if ( sharedPaths.isNull() )
3845 return QVariant();
3846
3847 return QVariant::fromValue( sharedPaths );
3848}
3849
3850
3851static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3852{
3853 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3854
3855 if ( geom.isNull() )
3856 return QVariant();
3857
3858 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3859
3860 QgsGeometry simplified = geom.simplify( tolerance );
3861 if ( simplified.isNull() )
3862 return QVariant();
3863
3864 return simplified;
3865}
3866
3867static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3868{
3869 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3870
3871 if ( geom.isNull() )
3872 return QVariant();
3873
3874 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3875
3877
3878 QgsGeometry simplified = simplifier.simplify( geom );
3879 if ( simplified.isNull() )
3880 return QVariant();
3881
3882 return simplified;
3883}
3884
3885static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3886{
3887 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3888
3889 if ( geom.isNull() )
3890 return QVariant();
3891
3892 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3893 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3894 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3895 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3896
3897 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3898 if ( smoothed.isNull() )
3899 return QVariant();
3900
3901 return smoothed;
3902}
3903
3904static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3905{
3906 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3907
3908 if ( geom.isNull() )
3909 return QVariant();
3910
3911 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3912 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3913 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3914
3915 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3916 if ( waved.isNull() )
3917 return QVariant();
3918
3919 return waved;
3920}
3921
3922static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3923{
3924 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3925
3926 if ( geom.isNull() )
3927 return QVariant();
3928
3929 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3930 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3931 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3932 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3933 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3934
3935 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
3936 if ( waved.isNull() )
3937 return QVariant();
3938
3939 return waved;
3940}
3941
3942static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3943{
3944 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3945
3946 if ( geom.isNull() )
3947 return QVariant();
3948
3949 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3950 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3951 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3952
3953 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3954 if ( waved.isNull() )
3955 return QVariant();
3956
3957 return waved;
3958}
3959
3960static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3961{
3962 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3963
3964 if ( geom.isNull() )
3965 return QVariant();
3966
3967 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3968 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3969 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3970 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3971 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3972
3973 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
3974 if ( waved.isNull() )
3975 return QVariant();
3976
3977 return waved;
3978}
3979
3980static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3981{
3982 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3983
3984 if ( geom.isNull() )
3985 return QVariant();
3986
3987 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3988 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3989 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3990
3991 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3992 if ( waved.isNull() )
3993 return QVariant();
3994
3995 return waved;
3996}
3997
3998static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3999{
4000 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4001
4002 if ( geom.isNull() )
4003 return QVariant();
4004
4005 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4006 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4007 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4008 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4009 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
4010
4011 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength, minAmplitude, maxAmplitude, seed );
4012 if ( waved.isNull() )
4013 return QVariant();
4014
4015 return waved;
4016}
4017
4018static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4019{
4020 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4021
4022 if ( geom.isNull() )
4023 return QVariant();
4024
4025 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
4026 QVector< double > dashPattern;
4027 dashPattern.reserve( pattern.size() );
4028 for ( const QVariant &value : std::as_const( pattern ) )
4029 {
4030 bool ok = false;
4031 double v = value.toDouble( &ok );
4032 if ( ok )
4033 {
4034 dashPattern << v;
4035 }
4036 else
4037 {
4038 parent->setEvalErrorString( u"Dash pattern must be an array of numbers"_s );
4039 return QgsGeometry();
4040 }
4041 }
4042
4043 if ( dashPattern.size() % 2 != 0 )
4044 {
4045 parent->setEvalErrorString( u"Dash pattern must contain an even number of elements"_s );
4046 return QgsGeometry();
4047 }
4048
4049 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
4051 if ( startRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4053 else if ( startRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4055 else if ( startRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4057 else if ( startRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4059 else if ( startRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4061 else
4062 {
4063 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( startRuleString ) );
4064 return QgsGeometry();
4065 }
4066
4067 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4069 if ( endRuleString.compare( "no_rule"_L1, Qt::CaseInsensitive ) == 0 )
4071 else if ( endRuleString.compare( "full_dash"_L1, Qt::CaseInsensitive ) == 0 )
4073 else if ( endRuleString.compare( "half_dash"_L1, Qt::CaseInsensitive ) == 0 )
4075 else if ( endRuleString.compare( "full_gap"_L1, Qt::CaseInsensitive ) == 0 )
4077 else if ( endRuleString.compare( "half_gap"_L1, Qt::CaseInsensitive ) == 0 )
4079 else
4080 {
4081 parent->setEvalErrorString( u"'%1' is not a valid dash pattern rule"_s.arg( endRuleString ) );
4082 return QgsGeometry();
4083 }
4084
4085 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4087 if ( adjustString.compare( "both"_L1, Qt::CaseInsensitive ) == 0 )
4089 else if ( adjustString.compare( "dash"_L1, Qt::CaseInsensitive ) == 0 )
4091 else if ( adjustString.compare( "gap"_L1, Qt::CaseInsensitive ) == 0 )
4093 else
4094 {
4095 parent->setEvalErrorString( u"'%1' is not a valid dash pattern size adjustment"_s.arg( adjustString ) );
4096 return QgsGeometry();
4097 }
4098
4099 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4100
4101 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
4102 if ( result.isNull() )
4103 return QVariant();
4104
4105 return result;
4106}
4107
4108static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4109{
4110 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4111
4112 if ( geom.isNull() )
4113 return QVariant();
4114
4115 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
4116 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
4117 if ( densified.isNull() )
4118 return QVariant();
4119
4120 return densified;
4121}
4122
4123static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4124{
4125 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4126
4127 if ( geom.isNull() )
4128 return QVariant();
4129
4130 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4131 const QgsGeometry densified = geom.densifyByDistance( distance );
4132 if ( densified.isNull() )
4133 return QVariant();
4134
4135 return densified;
4136}
4137
4138static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4139{
4140 QVariantList list;
4141 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
4142 {
4143 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
4144 }
4145 else
4146 {
4147 list = values;
4148 }
4149
4150 QVector< QgsGeometry > parts;
4151 parts.reserve( list.size() );
4152 for ( const QVariant &value : std::as_const( list ) )
4153 {
4154 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
4155 if ( part.isNull() )
4156 return QgsGeometry();
4157 parts << part;
4158 }
4159
4160 return QgsGeometry::collectGeometry( parts );
4161}
4162
4163static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4164{
4165 if ( values.count() < 2 || values.count() > 4 )
4166 {
4167 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
4168 return QVariant();
4169 }
4170
4171 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4172 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4173 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
4174 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
4175 switch ( values.count() )
4176 {
4177 case 2:
4178 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
4179 case 3:
4180 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
4181 case 4:
4182 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
4183 }
4184 return QVariant(); //avoid warning
4185}
4186
4187static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4188{
4189 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
4190 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4191 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4192 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
4193}
4194
4195static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4196{
4197 if ( values.empty() )
4198 {
4199 return QVariant();
4200 }
4201
4202 QVector<QgsPoint> points;
4203 points.reserve( values.count() );
4204
4205 auto addPoint = [&points]( const QgsGeometry &geom ) {
4206 if ( geom.isNull() )
4207 return;
4208
4209 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4210 return;
4211
4213 if ( !point )
4214 return;
4215
4216 points << *point;
4217 };
4218
4219 for ( const QVariant &value : values )
4220 {
4221 if ( value.userType() == QMetaType::Type::QVariantList )
4222 {
4223 const QVariantList list = value.toList();
4224 for ( const QVariant &v : list )
4225 {
4226 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
4227 }
4228 }
4229 else
4230 {
4231 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
4232 }
4233 }
4234
4235 if ( points.count() < 2 )
4236 return QVariant();
4237
4238 return QgsGeometry( new QgsLineString( points ) );
4239}
4240
4241static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4242{
4243 if ( values.count() < 1 )
4244 {
4245 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
4246 return QVariant();
4247 }
4248
4249 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4250
4251 if ( outerRing.type() == Qgis::GeometryType::Polygon )
4252 return outerRing; // if it's already a polygon we have nothing to do
4253
4254 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
4255 return QVariant();
4256
4257 auto polygon = std::make_unique< QgsPolygon >();
4258
4259 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4260 if ( !exteriorRing && outerRing.isMultipart() )
4261 {
4263 {
4264 if ( collection->numGeometries() == 1 )
4265 {
4266 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4267 }
4268 }
4269 }
4270
4271 if ( !exteriorRing )
4272 return QVariant();
4273
4274 polygon->setExteriorRing( exteriorRing->segmentize() );
4275
4276
4277 for ( int i = 1; i < values.count(); ++i )
4278 {
4279 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4280 if ( ringGeom.isNull() )
4281 continue;
4282
4283 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4284 continue;
4285
4286 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4287 if ( !ring && ringGeom.isMultipart() )
4288 {
4290 {
4291 if ( collection->numGeometries() == 1 )
4292 {
4293 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4294 }
4295 }
4296 }
4297
4298 if ( !ring )
4299 continue;
4300
4301 polygon->addInteriorRing( ring->segmentize() );
4302 }
4303
4304 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4305}
4306
4307static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4308{
4309 auto tr = std::make_unique<QgsTriangle>();
4310 auto lineString = std::make_unique<QgsLineString>();
4311 lineString->clear();
4312
4313 for ( const QVariant &value : values )
4314 {
4315 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4316 if ( geom.isNull() )
4317 return QVariant();
4318
4319 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4320 return QVariant();
4321
4323 if ( !point && geom.isMultipart() )
4324 {
4326 {
4327 if ( collection->numGeometries() == 1 )
4328 {
4329 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4330 }
4331 }
4332 }
4333
4334 if ( !point )
4335 return QVariant();
4336
4337 lineString->addVertex( *point );
4338 }
4339
4340 tr->setExteriorRing( lineString.release() );
4341
4342 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4343}
4344
4345static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4346{
4347 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4348 if ( geom.isNull() )
4349 return QVariant();
4350
4351 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4352 return QVariant();
4353
4354 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4355 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4356
4357 if ( segment < 3 )
4358 {
4359 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4360 return QVariant();
4361 }
4363 if ( !point && geom.isMultipart() )
4364 {
4366 {
4367 if ( collection->numGeometries() == 1 )
4368 {
4369 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4370 }
4371 }
4372 }
4373 if ( !point )
4374 return QVariant();
4375
4376 QgsCircle circ( *point, radius );
4377 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4378}
4379
4380static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4381{
4382 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4383 if ( geom.isNull() )
4384 return QVariant();
4385
4386 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4387 return QVariant();
4388
4389 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4390 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4391 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4392 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4393 if ( segment < 3 )
4394 {
4395 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4396 return QVariant();
4397 }
4399 if ( !point && geom.isMultipart() )
4400 {
4402 {
4403 if ( collection->numGeometries() == 1 )
4404 {
4405 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4406 }
4407 }
4408 }
4409 if ( !point )
4410 return QVariant();
4411
4412 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4413 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4414}
4415
4416static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4417{
4418 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4419 if ( pt1.isNull() )
4420 return QVariant();
4421
4422 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4423 return QVariant();
4424
4425 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4426 if ( pt2.isNull() )
4427 return QVariant();
4428
4429 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4430 return QVariant();
4431
4432 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4433 if ( nbEdges < 3 )
4434 {
4435 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4436 return QVariant();
4437 }
4438
4439 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4441 {
4442 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4443 return QVariant();
4444 }
4445
4447 if ( !center && pt1.isMultipart() )
4448 {
4450 {
4451 if ( collection->numGeometries() == 1 )
4452 {
4453 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4454 }
4455 }
4456 }
4457 if ( !center )
4458 return QVariant();
4459
4461 if ( !corner && pt2.isMultipart() )
4462 {
4464 {
4465 if ( collection->numGeometries() == 1 )
4466 {
4467 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4468 }
4469 }
4470 }
4471 if ( !corner )
4472 return QVariant();
4473
4474 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4475
4476 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4477}
4478
4479static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4480{
4481 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4482 if ( pt1.isNull() )
4483 return QVariant();
4484 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4485 return QVariant();
4486
4487 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4488 if ( pt2.isNull() )
4489 return QVariant();
4490 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4491 return QVariant();
4492
4493 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4494 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4495 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4496
4497 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4498}
4499
4500static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4501{
4502 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4503 if ( pt1.isNull() )
4504 return QVariant();
4505 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4506 return QVariant();
4507
4508 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4509 if ( pt2.isNull() )
4510 return QVariant();
4511 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4512 return QVariant();
4513
4514 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4515 if ( pt3.isNull() )
4516 return QVariant();
4517 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4518 return QVariant();
4519
4520 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4521 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4522 {
4523 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4524 return QVariant();
4525 }
4526 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4527 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4528 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4529 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4530 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4531}
4532
4533static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4534{
4535 if ( geom.isNull() )
4536 return QVariant();
4537
4538 if ( idx < 0 )
4539 {
4540 idx += geom.constGet()->nCoordinates();
4541 }
4542 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4543 {
4544 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4545 return QVariant();
4546 }
4547 return QVariant::fromValue( geom.vertexAt( idx ) );
4548}
4549
4550// function used for the old $ style
4551static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4552{
4553 FEAT_FROM_CONTEXT( context, feature )
4554 const QgsGeometry geom = feature.geometry();
4555 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4556
4557 const QVariant v = pointAt( geom, idx, parent );
4558
4559 if ( !v.isNull() )
4560 return QVariant( v.value<QgsPoint>().x() );
4561 else
4562 return QVariant();
4563}
4564static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4565{
4566 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))
4567 {
4568 return fcnOldXat( values, f, parent, node );
4569 }
4570 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)
4571 {
4572 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4573 }
4574
4575 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4576 if ( geom.isNull() )
4577 {
4578 return QVariant();
4579 }
4580
4581 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4582
4583 const QVariant v = pointAt( geom, vertexNumber, parent );
4584 if ( !v.isNull() )
4585 return QVariant( v.value<QgsPoint>().x() );
4586 else
4587 return QVariant();
4588}
4589
4590// function used for the old $ style
4591static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4592{
4593 FEAT_FROM_CONTEXT( context, feature )
4594 const QgsGeometry geom = feature.geometry();
4595 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4596
4597 const QVariant v = pointAt( geom, idx, parent );
4598
4599 if ( !v.isNull() )
4600 return QVariant( v.value<QgsPoint>().y() );
4601 else
4602 return QVariant();
4603}
4604static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4605{
4606 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))
4607 {
4608 return fcnOldYat( values, f, parent, node );
4609 }
4610 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)
4611 {
4612 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4613 }
4614
4615 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4616 if ( geom.isNull() )
4617 {
4618 return QVariant();
4619 }
4620
4621 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4622
4623 const QVariant v = pointAt( geom, vertexNumber, parent );
4624 if ( !v.isNull() )
4625 return QVariant( v.value<QgsPoint>().y() );
4626 else
4627 return QVariant();
4628}
4629
4630static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4631{
4632 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4633 if ( geom.isNull() )
4634 {
4635 return QVariant();
4636 }
4637
4638 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4639
4640 const QVariant v = pointAt( geom, vertexNumber, parent );
4641 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4642 return QVariant( v.value<QgsPoint>().z() );
4643 else
4644 return QVariant();
4645}
4646
4647static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4648{
4649 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4650 if ( geom.isNull() )
4651 {
4652 return QVariant();
4653 }
4654
4655 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4656
4657 const QVariant v = pointAt( geom, vertexNumber, parent );
4658 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4659 return QVariant( v.value<QgsPoint>().m() );
4660 else
4661 return QVariant();
4662}
4663
4664
4665static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4666{
4667 if ( !context )
4668 return QVariant();
4669
4670 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4671 if ( context->hasGeometry() )
4672 return context->geometry();
4673 else
4674 {
4675 FEAT_FROM_CONTEXT( context, f )
4676 QgsGeometry geom = f.geometry();
4677 if ( !geom.isNull() )
4678 return QVariant::fromValue( geom );
4679 else
4680 return QVariant();
4681 }
4682}
4683
4684static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4685{
4686 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4687 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4688 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4689 return result;
4690}
4691
4692static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4693{
4694 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4695 if ( wkb.isNull() )
4696 return QVariant();
4697
4698 QgsGeometry geom;
4699 geom.fromWkb( wkb );
4700 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4701}
4702
4703static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4704{
4705 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4706 QgsOgcUtils::Context ogcContext;
4707 if ( context )
4708 {
4709 QgsWeakMapLayerPointer mapLayerPtr { context->variable( u"layer"_s ).value<QgsWeakMapLayerPointer>() };
4710 if ( mapLayerPtr )
4711 {
4712 ogcContext.layer = mapLayerPtr.data();
4713 ogcContext.transformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
4714 }
4715 }
4716 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4717 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4718 return result;
4719}
4720
4721static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4722{
4723 FEAT_FROM_CONTEXT( context, f )
4725 QgsDistanceArea *calc = parent->geomCalculator();
4726 if ( calc )
4727 {
4728 try
4729 {
4730 double area = calc->measureArea( f.geometry() );
4731 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4732 return QVariant( area );
4733 }
4734 catch ( QgsCsException & )
4735 {
4736 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4737 return QVariant();
4738 }
4739 }
4740 else
4741 {
4742 return QVariant( f.geometry().area() );
4743 }
4744}
4745
4746static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4747{
4748 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4749
4750 if ( geom.type() != Qgis::GeometryType::Polygon )
4751 return QVariant();
4752
4753 return QVariant( geom.area() );
4754}
4755
4756static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4757{
4758 FEAT_FROM_CONTEXT( context, f )
4760 QgsDistanceArea *calc = parent->geomCalculator();
4761 if ( calc )
4762 {
4763 try
4764 {
4765 double len = calc->measureLength( f.geometry() );
4766 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4767 return QVariant( len );
4768 }
4769 catch ( QgsCsException & )
4770 {
4771 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4772 return QVariant();
4773 }
4774 }
4775 else
4776 {
4777 return QVariant( f.geometry().length() );
4778 }
4779}
4780
4781static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4782{
4783 FEAT_FROM_CONTEXT( context, f )
4785 QgsDistanceArea *calc = parent->geomCalculator();
4786 if ( calc )
4787 {
4788 try
4789 {
4790 double len = calc->measurePerimeter( f.geometry() );
4791 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4792 return QVariant( len );
4793 }
4794 catch ( QgsCsException & )
4795 {
4796 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4797 return QVariant();
4798 }
4799 }
4800 else
4801 {
4802 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4803 }
4804}
4805
4806static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4807{
4808 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4809
4810 if ( geom.type() != Qgis::GeometryType::Polygon )
4811 return QVariant();
4812
4813 //length for polygons = perimeter
4814 return QVariant( geom.length() );
4815}
4816
4817static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4818{
4819 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4820 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4821}
4822
4823static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4824{
4825 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4826 if ( geom.isNull() )
4827 return QVariant();
4828
4829 return QVariant( geom.constGet()->partCount() );
4830}
4831
4832static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4833{
4834 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4835 if ( geom.isNull() )
4836 return QVariant();
4837
4838 return QVariant( geom.isMultipart() );
4839}
4840
4841static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4842{
4843 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4844
4845 if ( geom.isNull() )
4846 return QVariant();
4847
4849 if ( curvePolygon )
4850 return QVariant( curvePolygon->numInteriorRings() );
4851
4853 if ( collection )
4854 {
4855 //find first CurvePolygon in collection
4856 for ( int i = 0; i < collection->numGeometries(); ++i )
4857 {
4858 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4859 if ( !curvePolygon )
4860 continue;
4861
4862 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4863 }
4864 }
4865
4866 return QVariant();
4867}
4868
4869static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4870{
4871 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4872
4873 if ( geom.isNull() )
4874 return QVariant();
4875
4877 if ( curvePolygon )
4878 return QVariant( curvePolygon->ringCount() );
4879
4880 bool foundPoly = false;
4881 int ringCount = 0;
4883 if ( collection )
4884 {
4885 //find CurvePolygons in collection
4886 for ( int i = 0; i < collection->numGeometries(); ++i )
4887 {
4888 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4889 if ( !curvePolygon )
4890 continue;
4891
4892 foundPoly = true;
4893 ringCount += curvePolygon->ringCount();
4894 }
4895 }
4896
4897 if ( !foundPoly )
4898 return QVariant();
4899
4900 return QVariant( ringCount );
4901}
4902
4903static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4904{
4905 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4906 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4907 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4908 return result;
4909}
4910
4911static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4912{
4913 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4914 return QVariant::fromValue( geom.boundingBox().width() );
4915}
4916
4917static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4918{
4919 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4920 return QVariant::fromValue( geom.boundingBox().height() );
4921}
4922
4923static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4924{
4925 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4926 if ( geom.isNull() )
4927 return QVariant();
4928
4930}
4931
4932static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4933{
4934 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4935 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4936}
4937
4938static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4939{
4940 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4941 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4942}
4943
4944static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4945{
4946 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4947 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4948}
4949
4950static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4951{
4952 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4953 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4954}
4955
4956static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4957{
4958 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4959
4960 if ( geom.isNull() || geom.isEmpty() )
4961 return QVariant();
4962
4963 if ( !geom.constGet()->is3D() )
4964 return QVariant();
4965
4966 double max = std::numeric_limits< double >::lowest();
4967
4968 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4969 {
4970 double z = ( *it ).z();
4971
4972 if ( max < z )
4973 max = z;
4974 }
4975
4976 if ( max == std::numeric_limits< double >::lowest() )
4977 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4978
4979 return QVariant( max );
4980}
4981
4982static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4983{
4984 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4985
4986 if ( geom.isNull() || geom.isEmpty() )
4987 return QVariant();
4988
4989 if ( !geom.constGet()->is3D() )
4990 return QVariant();
4991
4992 double min = std::numeric_limits< double >::max();
4993
4994 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4995 {
4996 double z = ( *it ).z();
4997
4998 if ( z < min )
4999 min = z;
5000 }
5001
5002 if ( min == std::numeric_limits< double >::max() )
5003 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5004
5005 return QVariant( min );
5006}
5007
5008static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5009{
5010 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5011
5012 if ( geom.isNull() || geom.isEmpty() )
5013 return QVariant();
5014
5015 if ( !geom.constGet()->isMeasure() )
5016 return QVariant();
5017
5018 double min = std::numeric_limits< double >::max();
5019
5020 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5021 {
5022 double m = ( *it ).m();
5023
5024 if ( m < min )
5025 min = m;
5026 }
5027
5028 if ( min == std::numeric_limits< double >::max() )
5029 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5030
5031 return QVariant( min );
5032}
5033
5034static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5035{
5036 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5037
5038 if ( geom.isNull() || geom.isEmpty() )
5039 return QVariant();
5040
5041 if ( !geom.constGet()->isMeasure() )
5042 return QVariant();
5043
5044 double max = std::numeric_limits< double >::lowest();
5045
5046 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
5047 {
5048 double m = ( *it ).m();
5049
5050 if ( max < m )
5051 max = m;
5052 }
5053
5054 if ( max == std::numeric_limits< double >::lowest() )
5055 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
5056
5057 return QVariant( max );
5058}
5059
5060static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5061{
5062 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5064 if ( !curve )
5065 {
5066 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
5067 return QVariant();
5068 }
5069
5070 return QVariant( curve->sinuosity() );
5071}
5072
5073static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5074{
5075 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5076 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
5077 if ( !curve )
5078 {
5079 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
5080 return QVariant();
5081 }
5082
5083 return QVariant( curve->straightDistance2d() );
5084}
5085
5086static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5087{
5088 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5090
5091 if ( !poly )
5092 {
5093 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
5094 return QVariant();
5095 }
5096
5097 return QVariant( poly->roundness() );
5098}
5099
5100
5101static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5102{
5103 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5104 if ( geom.isNull() )
5105 return QVariant();
5106
5107 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
5108 flipped->swapXy();
5109 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
5110}
5111
5112static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5113{
5114 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5115 if ( fGeom.isNull() )
5116 return QVariant();
5117
5118 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
5119 if ( !curve && fGeom.isMultipart() )
5120 {
5122 {
5123 if ( collection->numGeometries() == 1 )
5124 {
5125 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5126 }
5127 }
5128 }
5129
5130 if ( !curve )
5131 return QVariant();
5132
5133 return QVariant::fromValue( curve->isClosed() );
5134}
5135
5136static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5137{
5138 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5139
5140 if ( geom.isNull() )
5141 return QVariant();
5142
5143 QVariant result;
5144 if ( !geom.isMultipart() )
5145 {
5147
5148 if ( !line )
5149 return QVariant();
5150
5151 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5152 closedLine->close();
5153
5154 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
5155 }
5156 else
5157 {
5159 if ( !collection )
5160 return QVariant();
5161
5162 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
5163
5164 for ( int i = 0; i < collection->numGeometries(); ++i )
5165 {
5166 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
5167 {
5168 std::unique_ptr< QgsLineString > closedLine( line->clone() );
5169 closedLine->close();
5170
5171 closed->addGeometry( closedLine.release() );
5172 }
5173 }
5174 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
5175 }
5176
5177 return result;
5178}
5179
5180static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5181{
5182 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5183 if ( fGeom.isNull() )
5184 return QVariant();
5185
5186 return QVariant::fromValue( fGeom.isEmpty() );
5187}
5188
5189static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5190{
5191 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5192 return QVariant::fromValue( true );
5193
5194 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5195 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
5196}
5197
5198static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5199{
5200 if ( values.length() < 2 || values.length() > 3 )
5201 return QVariant();
5202
5203 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5204 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5205
5206 if ( fGeom.isNull() || sGeom.isNull() )
5207 return QVariant();
5208
5209 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
5210
5211 if ( values.length() == 2 )
5212 {
5213 //two geometry arguments, return relation
5214 QString result = engine->relate( sGeom.constGet() );
5215 return QVariant::fromValue( result );
5216 }
5217 else
5218 {
5219 //three arguments, test pattern
5220 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5221 bool result = engine->relatePattern( sGeom.constGet(), pattern );
5222 return QVariant::fromValue( result );
5223 }
5224}
5225
5226static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5227{
5228 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5229 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5230 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
5231}
5232static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5233{
5234 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5235 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5236 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
5237}
5238static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5239{
5240 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5241 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5242 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
5243}
5244static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5245{
5246 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5247 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5248 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
5249}
5250static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5251{
5252 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5253 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5254 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
5255}
5256static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5257{
5258 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5259 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5260 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5261}
5262static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5263{
5264 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5265 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5266 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5267}
5268static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5269{
5270 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5271 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5272 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5273}
5274
5275static QVariant fcnEquals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5276{
5277 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5278 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5279 return fGeom.equals( sGeom ) ? TVL_True : TVL_False;
5280}
5281
5282static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5283{
5284 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5285 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5286 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5287 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5288 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5289 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5290
5292 if ( endCapString.compare( "flat"_L1, Qt::CaseInsensitive ) == 0 )
5293 capStyle = Qgis::EndCapStyle::Flat;
5294 else if ( endCapString.compare( "square"_L1, Qt::CaseInsensitive ) == 0 )
5295 capStyle = Qgis::EndCapStyle::Square;
5296
5298 if ( joinString.compare( "miter"_L1, Qt::CaseInsensitive ) == 0 )
5299 joinStyle = Qgis::JoinStyle::Miter;
5300 else if ( joinString.compare( "bevel"_L1, Qt::CaseInsensitive ) == 0 )
5301 joinStyle = Qgis::JoinStyle::Bevel;
5302
5303 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5304 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5305 return result;
5306}
5307
5308static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5309{
5310 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5311 const QgsGeometry reoriented = fGeom.forceRHR();
5312 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5313}
5314
5315static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5316{
5317 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5318 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5319 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5320}
5321
5322static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5323{
5324 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5325 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5326 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5327}
5328
5329static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5330{
5331 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5333 if ( !pt && fGeom.isMultipart() )
5334 {
5336 {
5337 if ( collection->numGeometries() == 1 )
5338 {
5339 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5340 }
5341 }
5342 }
5343
5344 if ( !pt )
5345 {
5346 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5347 return QVariant();
5348 }
5349
5350 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5351 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5352 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5353 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5354
5355 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5356 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5357 return result;
5358}
5359
5360static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5361{
5362 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5363 if ( fGeom.type() != Qgis::GeometryType::Line )
5364 {
5365 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5366 return QVariant();
5367 }
5368
5369 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5370 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5371 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5372
5373 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5374 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5375 return result;
5376}
5377
5378static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5379{
5380 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5381 if ( fGeom.type() != Qgis::GeometryType::Line )
5382 {
5383 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5384 return QVariant();
5385 }
5386
5387 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5388
5389 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5390 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5391 return result;
5392}
5393
5394static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5395{
5396 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5397 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5398 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5399 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5400 if ( joinInt < 1 || joinInt > 3 )
5401 return QVariant();
5402 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5403
5404 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5405
5406 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5407 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5408 return result;
5409}
5410
5411static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5412{
5413 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5414 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5415 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5416
5417 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5418 if ( joinInt < 1 || joinInt > 3 )
5419 return QVariant();
5420 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5421
5422 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5423
5424 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5425 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5426 return result;
5427}
5428
5429static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5430{
5431 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5432 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5433 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5434
5435 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5436 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5437 return result;
5438}
5439
5440static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5441{
5442 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5443 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5444 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5445 fGeom.translate( dx, dy );
5446 return QVariant::fromValue( fGeom );
5447}
5448
5449static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5450{
5451 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5452 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5453 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent ) : QgsGeometry();
5454 const bool perPart = values.value( 3 ).toBool();
5455
5456 if ( center.isNull() && perPart && fGeom.isMultipart() )
5457 {
5458 // no explicit center, rotating per part
5459 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5460 // the result is equivalent to setting perPart as false anyway)
5461 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5462 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5463 {
5464 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5465 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5466 t.rotate( -rotation );
5467 t.translate( -partCenter.x(), -partCenter.y() );
5468 ( *it )->transform( t );
5469 }
5470 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5471 }
5472 else
5473 {
5474 QgsPointXY pt;
5475 if ( center.isEmpty() )
5476 {
5477 // if center wasn't specified, use bounding box centroid
5478 pt = fGeom.boundingBox().center();
5479 }
5481 {
5482 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5483 return QVariant();
5484 }
5485 else
5486 {
5488 }
5489
5490 fGeom.rotate( rotation, pt );
5491 return QVariant::fromValue( fGeom );
5492 }
5493}
5494
5495static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5496{
5497 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5498 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5499 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5500 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent ) : QgsGeometry();
5501
5502 QgsPointXY pt;
5503 if ( center.isNull() )
5504 {
5505 // if center wasn't specified, use bounding box centroid
5506 pt = fGeom.boundingBox().center();
5507 }
5509 {
5510 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5511 return QVariant();
5512 }
5513 else
5514 {
5515 pt = center.asPoint();
5516 }
5517
5518 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5519 t.scale( xScale, yScale );
5520 t.translate( -pt.x(), -pt.y() );
5521 fGeom.transform( t );
5522 return QVariant::fromValue( fGeom );
5523}
5524
5525static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5526{
5527 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5528 if ( fGeom.isNull() )
5529 {
5530 return QVariant();
5531 }
5532
5533 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5534 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5535
5536 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5537
5538 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5539 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5540
5541 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5542 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5543 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5544 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5545
5546 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5547 {
5548 fGeom.get()->addZValue( 0 );
5549 }
5550 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5551 {
5552 fGeom.get()->addMValue( 0 );
5553 }
5554
5555 QTransform transform;
5556 transform.translate( deltaX, deltaY );
5557 transform.rotate( rotationZ );
5558 transform.scale( scaleX, scaleY );
5559 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5560
5561 return QVariant::fromValue( fGeom );
5562}
5563
5564
5565static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5566{
5567 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5568 QgsGeometry geom = fGeom.centroid();
5569 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5570 return result;
5571}
5572static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5573{
5574 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5575 QgsGeometry geom = fGeom.pointOnSurface();
5576 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5577 return result;
5578}
5579
5580static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5581{
5582 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5583 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5584 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5585 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5586 return result;
5587}
5588
5589static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5590{
5591 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5592 QgsGeometry geom = fGeom.convexHull();
5593 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5594 return result;
5595}
5596
5597#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
5598static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5599{
5600 try
5601 {
5602 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5603 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5604 const bool allowHoles = values.value( 2 ).toBool();
5605 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5606 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5607 return result;
5608 }
5609 catch ( QgsCsException &cse )
5610 {
5611 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5612 return QVariant();
5613 }
5614}
5615#endif
5616
5617static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5618{
5619 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5620 int segments = 36;
5621 if ( values.length() == 2 )
5622 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5623 if ( segments < 0 )
5624 {
5625 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5626 return QVariant();
5627 }
5628
5629 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5630 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5631 return result;
5632}
5633
5634static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5635{
5636 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5638 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5639 return result;
5640}
5641
5642static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5643{
5644 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5645
5646 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5647 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5648 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5649
5650 double area, angle, width, height;
5651 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5652
5653 if ( geom.isNull() )
5654 {
5655 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5656 return QVariant();
5657 }
5658 return angle;
5659}
5660
5661static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5662{
5663 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5664 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5665 QgsGeometry geom = fGeom.difference( sGeom );
5666 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5667 return result;
5668}
5669
5670static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5671{
5672 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5673 return QVariant();
5674
5675 // two variants, one for geometry, one for string
5676
5677 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5678 if ( !fGeom.isNull() )
5679 {
5680 QVariant result;
5681 if ( !fGeom.isMultipart() )
5682 {
5683 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5684 if ( !curve )
5685 return QVariant();
5686
5687 QgsCurve *reversed = curve->reversed();
5688 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5689 }
5690 else
5691 {
5693 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5694 for ( int i = 0; i < collection->numGeometries(); ++i )
5695 {
5696 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5697 {
5698 reversed->addGeometry( curve->reversed() );
5699 }
5700 else
5701 {
5702 reversed->addGeometry( collection->geometryN( i )->clone() );
5703 }
5704 }
5705 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5706 }
5707 return result;
5708 }
5709
5710 //fall back to string variant
5711 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5712 std::reverse( string.begin(), string.end() );
5713 return string;
5714}
5715
5716static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5717{
5718 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5719 if ( fGeom.isNull() )
5720 return QVariant();
5721
5723 if ( !curvePolygon && fGeom.isMultipart() )
5724 {
5726 {
5727 if ( collection->numGeometries() == 1 )
5728 {
5729 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5730 }
5731 }
5732 }
5733
5734 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5735 return QVariant();
5736
5737 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5738 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5739 return result;
5740}
5741
5742static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5743{
5744 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5745 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5746 return QVariant( fGeom.distance( sGeom ) );
5747}
5748
5749static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5750{
5751 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5752 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5753
5754 double res = -1;
5755 if ( values.length() == 3 && values.at( 2 ).isValid() )
5756 {
5757 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5758 densify = std::clamp( densify, 0.0, 1.0 );
5759 res = g1.hausdorffDistanceDensify( g2, densify );
5760 }
5761 else
5762 {
5763 res = g1.hausdorffDistance( g2 );
5764 }
5765
5766 return res > -1 ? QVariant( res ) : QVariant();
5767}
5768
5769static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5770{
5771 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5772 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5773 QgsGeometry geom = fGeom.intersection( sGeom );
5774 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5775 return result;
5776}
5777static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5778{
5779 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5780 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5781 QgsGeometry geom = fGeom.symDifference( sGeom );
5782 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5783 return result;
5784}
5785static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5786{
5787 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5788 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5789 QgsGeometry geom = fGeom.combine( sGeom );
5790 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5791 return result;
5792}
5793
5794static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5795{
5796 if ( values.length() < 1 || values.length() > 2 )
5797 return QVariant();
5798
5799 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5800 int prec = 8;
5801 if ( values.length() == 2 )
5802 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5803 QString wkt = fGeom.asWkt( prec );
5804 return QVariant( wkt );
5805}
5806
5807static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5808{
5809 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5810 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5811}
5812
5813static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5814{
5815 if ( values.length() != 2 )
5816 {
5817 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5818 return QVariant();
5819 }
5820
5821 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5822 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5823
5824 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5825 if ( !pt1 && fGeom1.isMultipart() )
5826 {
5828 {
5829 if ( collection->numGeometries() == 1 )
5830 {
5831 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5832 }
5833 }
5834 }
5835
5836 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5837 if ( !pt2 && fGeom2.isMultipart() )
5838 {
5840 {
5841 if ( collection->numGeometries() == 1 )
5842 {
5843 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5844 }
5845 }
5846 }
5847
5848 if ( !pt1 || !pt2 )
5849 {
5850 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5851 return QVariant();
5852 }
5853
5854 // Code from PostGIS
5855 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5856 {
5857 if ( pt1->y() < pt2->y() )
5858 return 0.0;
5859 else if ( pt1->y() > pt2->y() )
5860 return M_PI;
5861 else
5862 return 0;
5863 }
5864
5865 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5866 {
5867 if ( pt1->x() < pt2->x() )
5868 return M_PI_2;
5869 else if ( pt1->x() > pt2->x() )
5870 return M_PI + ( M_PI_2 );
5871 else
5872 return 0;
5873 }
5874
5875 if ( pt1->x() < pt2->x() )
5876 {
5877 if ( pt1->y() < pt2->y() )
5878 {
5879 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5880 }
5881 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5882 {
5883 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI_2 );
5884 }
5885 }
5886
5887 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5888 {
5889 if ( pt1->y() > pt2->y() )
5890 {
5891 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) ) + M_PI;
5892 }
5893 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5894 {
5895 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) ) + ( M_PI + ( M_PI_2 ) );
5896 }
5897 }
5898}
5899
5900static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5901{
5902 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5903 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5904 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
5905 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5906
5907 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5908 {
5909 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5910 return QVariant();
5911 }
5912
5913 const QgsPointXY point1 = geom1.asPoint();
5914 const QgsPointXY point2 = geom2.asPoint();
5915 if ( point1.isEmpty() || point2.isEmpty() )
5916 {
5917 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5918 return QVariant();
5919 }
5920
5922 if ( context )
5923 {
5924 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
5925
5926 if ( !sourceCrs.isValid() )
5927 {
5928 sourceCrs = context->variable( u"_layer_crs"_s ).value<QgsCoordinateReferenceSystem>();
5929 }
5930
5931 if ( ellipsoid.isEmpty() )
5932 {
5933 ellipsoid = context->variable( u"project_ellipsoid"_s ).toString();
5934 }
5935 }
5936
5937 if ( !sourceCrs.isValid() )
5938 {
5939 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5940 return QVariant();
5941 }
5942
5943 QgsDistanceArea da;
5944 da.setSourceCrs( sourceCrs, tContext );
5945 if ( !da.setEllipsoid( ellipsoid ) )
5946 {
5947 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5948 return QVariant();
5949 }
5950
5951 try
5952 {
5953 const double bearing = da.bearing( point1, point2 );
5954 if ( std::isfinite( bearing ) )
5955 {
5956 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5957 }
5958 }
5959 catch ( QgsCsException &cse )
5960 {
5961 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5962 return QVariant();
5963 }
5964 return QVariant();
5965}
5966
5967static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5968{
5969 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5970
5972 {
5973 parent->setEvalErrorString( u"'project' requires a point geometry"_s );
5974 return QVariant();
5975 }
5976
5977 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5978 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5979 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5980
5981 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef() );
5982 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5983
5984 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5985}
5986
5987static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5988{
5989 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5990 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5991
5992 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5993 if ( !pt1 && fGeom1.isMultipart() )
5994 {
5996 {
5997 if ( collection->numGeometries() == 1 )
5998 {
5999 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6000 }
6001 }
6002 }
6003 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
6004 if ( !pt2 && fGeom2.isMultipart() )
6005 {
6007 {
6008 if ( collection->numGeometries() == 1 )
6009 {
6010 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
6011 }
6012 }
6013 }
6014
6015 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) || !pt1 || !pt2 )
6016 {
6017 parent->setEvalErrorString( u"Function 'inclination' requires two points as arguments."_s );
6018 return QVariant();
6019 }
6020
6021 return pt1->inclination( *pt2 );
6022}
6023
6024static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6025{
6026 if ( values.length() != 3 )
6027 return QVariant();
6028
6029 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6030 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6031 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6032
6033 QgsGeometry geom = fGeom.extrude( x, y );
6034
6035 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
6036 return result;
6037}
6038
6039static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
6040{
6041 if ( values.length() < 2 )
6042 return QVariant();
6043
6044 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6045
6046 if ( !fGeom.isMultipart() )
6047 return values.at( 0 );
6048
6049 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6050 QVariant cachedExpression;
6051 if ( ctx )
6052 cachedExpression = ctx->cachedValue( expString );
6053 QgsExpression expression;
6054
6055 if ( cachedExpression.isValid() )
6056 {
6057 expression = cachedExpression.value<QgsExpression>();
6058 }
6059 else
6060 expression = QgsExpression( expString );
6061
6062 bool asc = values.value( 2 ).toBool();
6063
6064 QgsExpressionContext *unconstedContext = nullptr;
6065 QgsFeature f;
6066 if ( ctx )
6067 {
6068 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
6069 // so no reason to worry
6070 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
6071 f = ctx->feature();
6072 }
6073 else
6074 {
6075 // If there's no context provided, create a fake one
6076 unconstedContext = new QgsExpressionContext();
6077 }
6078
6080 Q_ASSERT( collection ); // Should have failed the multipart check above
6081
6083 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
6084 QgsExpressionSorter sorter( orderBy );
6085
6086 QList<QgsFeature> partFeatures;
6087 partFeatures.reserve( collection->partCount() );
6088 for ( int i = 0; i < collection->partCount(); ++i )
6089 {
6090 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
6091 partFeatures << f;
6092 }
6093
6094 sorter.sortFeatures( partFeatures, unconstedContext );
6095
6097
6098 Q_ASSERT( orderedGeom );
6099
6100 while ( orderedGeom->partCount() )
6101 orderedGeom->removeGeometry( 0 );
6102
6103 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
6104 {
6105 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
6106 }
6107
6108 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
6109
6110 if ( !ctx )
6111 delete unconstedContext;
6112
6113 return result;
6114}
6115
6116static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6117{
6118 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6119 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6120
6121 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
6122
6123 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6124 return result;
6125}
6126
6127static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6128{
6129 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6130 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6131
6132 QgsGeometry geom = fromGeom.shortestLine( toGeom );
6133
6134 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6135 return result;
6136}
6137
6138static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6139{
6140 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6141 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6142
6143 QgsGeometry geom = lineGeom.interpolate( distance );
6144
6145 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
6146 return result;
6147}
6148
6149static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6150{
6151 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6152 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6153 const bool use3DDistance = values.at( 2 ).toBool();
6154
6155 double x, y, z, distance;
6156
6158 if ( !line )
6159 {
6160 return QVariant();
6161 }
6162
6163 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
6164 {
6165 QgsPoint point( x, y );
6166 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
6167 {
6168 point.addZValue( z );
6169 }
6170 return QVariant::fromValue( QgsGeometry( point.clone() ) );
6171 }
6172
6173 return QVariant();
6174}
6175
6176static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6177{
6178 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6179 if ( lineGeom.type() != Qgis::GeometryType::Line )
6180 {
6181 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
6182 return QVariant();
6183 }
6184
6185 const QgsCurve *curve = nullptr;
6186 if ( !lineGeom.isMultipart() )
6187 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
6188 else
6189 {
6191 {
6192 if ( collection->numGeometries() > 0 )
6193 {
6194 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
6195 }
6196 }
6197 }
6198 if ( !curve )
6199 return QVariant();
6200
6201 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6202 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6203
6204 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
6205 QgsGeometry result( std::move( substring ) );
6206 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
6207}
6208
6209static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6210{
6211 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6212 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6213
6214 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
6215}
6216
6217static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6218{
6219 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6220 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6221 if ( vertex < 0 )
6222 {
6223 //negative idx
6224 int count = geom.constGet()->nCoordinates();
6225 vertex = count + vertex;
6226 }
6227
6228 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
6229}
6230
6231static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6232{
6233 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6234 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6235 if ( vertex < 0 )
6236 {
6237 //negative idx
6238 int count = geom.constGet()->nCoordinates();
6239 vertex = count + vertex;
6240 }
6241
6242 return geom.distanceToVertex( vertex );
6243}
6244
6245static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6246{
6247 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6248 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
6249
6250 double distance = lineGeom.lineLocatePoint( pointGeom );
6251
6252 return distance >= 0 ? distance : QVariant();
6253}
6254
6255static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6256{
6257 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6258 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6259 const bool use3DDistance = values.at( 2 ).toBool();
6260
6261 double x, y, z, distance;
6262
6264 if ( !line )
6265 {
6266 return QVariant();
6267 }
6268
6269 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6270 return found ? distance : QVariant();
6271}
6272
6273static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6274{
6275 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6276 {
6277 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6278 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6279 }
6280
6281 if ( values.length() >= 1 )
6282 {
6283 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6284 return QVariant( qlonglong( std::round( number ) ) );
6285 }
6286
6287 return QVariant();
6288}
6289
6290static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6291{
6292 Q_UNUSED( values )
6293 Q_UNUSED( parent )
6294 return M_PI;
6295}
6296
6297static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6298{
6299 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6300 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6301 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6302 if ( places < 0 )
6303 {
6304 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6305 return QVariant();
6306 }
6307
6308 const bool omitGroupSeparator = values.value( 3 ).toBool();
6309 const bool trimTrailingZeros = values.value( 4 ).toBool();
6310
6311 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6312 if ( !omitGroupSeparator )
6313 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6314 else
6315 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6316
6317 QString res = locale.toString( value, 'f', places );
6318
6319 if ( trimTrailingZeros )
6320 {
6321 const QChar decimal = locale.decimalPoint().at( 0 );
6322 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6323
6324 if ( res.contains( decimal ) )
6325 {
6326 int trimPoint = res.length() - 1;
6327
6328 while ( res.at( trimPoint ) == zeroDigit )
6329 trimPoint--;
6330
6331 if ( res.at( trimPoint ) == decimal )
6332 trimPoint--;
6333
6334 res.truncate( trimPoint + 1 );
6335 }
6336 }
6337
6338 return res;
6339}
6340
6341static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6342{
6343 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6344 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6345 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6346
6347 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6348 if ( format.indexOf( "Z" ) > 0 )
6349 datetime = datetime.toUTC();
6350
6351 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6352 return locale.toString( datetime, format );
6353}
6354
6355static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6356{
6357 const QVariant variant = values.at( 0 );
6358 bool isQColor;
6359 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6360 if ( !color.isValid() )
6361 return QVariant();
6362
6363 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6364 if ( color.spec() == QColor::Spec::Cmyk )
6365 {
6366 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() )
6367 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6368 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6369 }
6370 else
6371 {
6372 const float avg = ( color.redF() + color.greenF() + color.blueF() )
6373 / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6374 color.setRgbF( avg, avg, avg, alpha );
6375 }
6376
6377 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6378}
6379
6380static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6381{
6382 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6383 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6384 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6385 if ( ratio > 1 )
6386 {
6387 ratio = 1;
6388 }
6389 else if ( ratio < 0 )
6390 {
6391 ratio = 0;
6392 }
6393
6394 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6395 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6396 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6397 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6398
6399 QColor newColor( red, green, blue, alpha );
6400
6401 return QgsSymbolLayerUtils::encodeColor( newColor );
6402}
6403
6404static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6405{
6406 const QVariant variant1 = values.at( 0 );
6407 const QVariant variant2 = values.at( 1 );
6408
6409 if ( variant1.userType() != variant2.userType() )
6410 {
6411 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6412 return QVariant();
6413 }
6414
6415 bool isQColor;
6416 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6417 if ( !color1.isValid() )
6418 return QVariant();
6419
6420 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6421 if ( !color2.isValid() )
6422 return QVariant();
6423
6424 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6425 {
6426 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6427 return QVariant();
6428 }
6429
6430 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6431
6432 // TODO QGIS 5 remove the nolint instructions, QColor was qreal (double) and is now float
6433 // NOLINTBEGIN(bugprone-narrowing-conversions)
6434
6435 QColor newColor;
6436 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6437 if ( color1.spec() == QColor::Spec::Cmyk )
6438 {
6439 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6440 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6441 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6442 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6443 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6444 }
6445 else
6446 {
6447 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6448 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6449 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6450 newColor = QColor::fromRgbF( red, green, blue, alpha );
6451 }
6452
6453 // NOLINTEND(bugprone-narrowing-conversions)
6454
6455 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6456}
6457
6458static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6459{
6460 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6461 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6462 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6463 QColor color = QColor( red, green, blue );
6464 if ( !color.isValid() )
6465 {
6466 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6467 color = QColor( 0, 0, 0 );
6468 }
6469
6470 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6471}
6472
6473static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6474{
6475 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6476 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6477 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6478 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6479 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6480 if ( !color.isValid() )
6481 {
6482 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6483 return QVariant();
6484 }
6485
6486 return color;
6487}
6488
6489static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6490{
6491 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6492 QVariant value = node->eval( parent, context );
6493 if ( parent->hasEvalError() )
6494 {
6495 parent->setEvalErrorString( QString() );
6496 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6498 value = node->eval( parent, context );
6500 }
6501 return value;
6502}
6503
6504static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6505{
6506 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6508 QVariant value = node->eval( parent, context );
6510 if ( value.toBool() )
6511 {
6512 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6514 value = node->eval( parent, context );
6516 }
6517 else
6518 {
6519 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6521 value = node->eval( parent, context );
6523 }
6524 return value;
6525}
6526
6527static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6528{
6529 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6530 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6531 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6532 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6533 QColor color = QColor( red, green, blue, alpha );
6534 if ( !color.isValid() )
6535 {
6536 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6537 color = QColor( 0, 0, 0 );
6538 }
6539 return QgsSymbolLayerUtils::encodeColor( color );
6540}
6541
6542QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6543{
6544 QgsGradientColorRamp expRamp;
6545 const QgsColorRamp *ramp = nullptr;
6546 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6547 {
6548 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6549 ramp = &expRamp;
6550 }
6551 else
6552 {
6553 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6554 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6555 if ( !ramp )
6557 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6558 return QVariant();
6559 }
6560 }
6561
6562 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6563 QColor color = ramp->color( value );
6564 return color;
6565}
6566
6567QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6568{
6569 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6570 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6571}
6572
6573static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6574{
6575 // Hue ranges from 0 - 360
6576 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6577 // Saturation ranges from 0 - 100
6578 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6579 // Lightness ranges from 0 - 100
6580 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6581
6582 QColor color = QColor::fromHslF( hue, saturation, lightness );
6583
6584 if ( !color.isValid() )
6585 {
6586 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6587 color = QColor( 0, 0, 0 );
6588 }
6589
6590 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6591}
6592
6593static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6594{
6595 // Hue ranges from 0 - 360
6596 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6597 // Saturation ranges from 0 - 100
6598 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6599 // Lightness ranges from 0 - 100
6600 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6601 // Alpha ranges from 0 - 255
6602 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6603
6604 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6605 if ( !color.isValid() )
6606 {
6607 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6608 color = QColor( 0, 0, 0 );
6609 }
6610 return QgsSymbolLayerUtils::encodeColor( color );
6611}
6612
6613static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6614{
6615 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6616 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6617 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6618 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6619
6620 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6621 if ( !color.isValid() )
6622 {
6623 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6624 return QVariant();
6625 }
6626
6627 return color;
6628}
6629
6630static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6631{
6632 // Hue ranges from 0 - 360
6633 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6634 // Saturation ranges from 0 - 100
6635 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6636 // Value ranges from 0 - 100
6637 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6638
6639 QColor color = QColor::fromHsvF( hue, saturation, value );
6640
6641 if ( !color.isValid() )
6642 {
6643 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6644 color = QColor( 0, 0, 0 );
6645 }
6646
6647 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6648}
6649
6650static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6651{
6652 // Hue ranges from 0 - 360
6653 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6654 // Saturation ranges from 0 - 100
6655 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6656 // Value ranges from 0 - 100
6657 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6658 // Alpha ranges from 0 - 255
6659 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6660
6661 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6662 if ( !color.isValid() )
6663 {
6664 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6665 color = QColor( 0, 0, 0 );
6666 }
6667 return QgsSymbolLayerUtils::encodeColor( color );
6668}
6669
6670static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6671{
6672 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6673 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6674 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6675 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6676 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6677
6678 if ( !color.isValid() )
6679 {
6680 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6681 return QVariant();
6682 }
6683
6684 return color;
6685}
6686
6687static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6688{
6689 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6690 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6691 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6692 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6693 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6694
6695 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6696 if ( !color.isValid() )
6697 {
6698 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6699 return QVariant();
6700 }
6701
6702 return color;
6703}
6704
6705static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6706{
6707 // Cyan ranges from 0 - 100
6708 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6709 // Magenta ranges from 0 - 100
6710 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6711 // Yellow ranges from 0 - 100
6712 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6713 // Black ranges from 0 - 100
6714 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6715
6716 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6717
6718 if ( !color.isValid() )
6719 {
6720 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6721 color = QColor( 0, 0, 0 );
6722 }
6723
6724 return u"%1,%2,%3"_s.arg( color.red() ).arg( color.green() ).arg( color.blue() );
6725}
6726
6727static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6728{
6729 // Cyan ranges from 0 - 100
6730 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6731 // Magenta ranges from 0 - 100
6732 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6733 // Yellow ranges from 0 - 100
6734 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6735 // Black ranges from 0 - 100
6736 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6737 // Alpha ranges from 0 - 255
6738 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6739
6740 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6741 if ( !color.isValid() )
6742 {
6743 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6744 color = QColor( 0, 0, 0 );
6745 }
6746 return QgsSymbolLayerUtils::encodeColor( color );
6747}
6748
6749static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6750{
6751 const QVariant variant = values.at( 0 );
6752 bool isQColor;
6753 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6754 if ( !color.isValid() )
6755 return QVariant();
6756
6757 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6758 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6759 return color.red();
6760 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6761 return color.green();
6762 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6763 return color.blue();
6764 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6765 return color.alpha();
6766 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6767 return static_cast< double >( color.hsvHueF() * 360 );
6768 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6769 return static_cast< double >( color.hsvSaturationF() * 100 );
6770 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6771 return static_cast< double >( color.valueF() * 100 );
6772 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6773 return static_cast< double >( color.hslHueF() * 360 );
6774 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6775 return static_cast< double >( color.hslSaturationF() * 100 );
6776 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6777 return static_cast< double >( color.lightnessF() * 100 );
6778 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6779 return static_cast< double >( color.cyanF() * 100 );
6780 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6781 return static_cast< double >( color.magentaF() * 100 );
6782 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6783 return static_cast< double >( color.yellowF() * 100 );
6784 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6785 return static_cast< double >( color.blackF() * 100 );
6786
6787 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6788 return QVariant();
6789}
6790
6791static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6792{
6793 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6794 if ( map.empty() )
6795 {
6796 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6797 return QVariant();
6798 }
6799
6800 QList< QColor > colors;
6802 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6803 {
6804 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6805 if ( !colors.last().isValid() )
6806 {
6807 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6808 return QVariant();
6809 }
6810
6811 double step = it.key().toDouble();
6812 if ( it == map.constBegin() )
6813 {
6814 if ( step != 0.0 )
6815 stops << QgsGradientStop( step, colors.last() );
6816 }
6817 else if ( it == map.constEnd() )
6818 {
6819 if ( step != 1.0 )
6820 stops << QgsGradientStop( step, colors.last() );
6821 }
6822 else
6823 {
6824 stops << QgsGradientStop( step, colors.last() );
6825 }
6826 }
6827 bool discrete = values.at( 1 ).toBool();
6828
6829 if ( colors.empty() )
6830 return QVariant();
6831
6832 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6833}
6834
6835static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6836{
6837 const QVariant variant = values.at( 0 );
6838 bool isQColor;
6839 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6840 if ( !color.isValid() )
6841 return QVariant();
6842
6843 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6844 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6845 if ( part.compare( "red"_L1, Qt::CaseInsensitive ) == 0 )
6846 color.setRed( std::clamp( value, 0, 255 ) );
6847 else if ( part.compare( "green"_L1, Qt::CaseInsensitive ) == 0 )
6848 color.setGreen( std::clamp( value, 0, 255 ) );
6849 else if ( part.compare( "blue"_L1, Qt::CaseInsensitive ) == 0 )
6850 color.setBlue( std::clamp( value, 0, 255 ) );
6851 else if ( part.compare( "alpha"_L1, Qt::CaseInsensitive ) == 0 )
6852 color.setAlpha( std::clamp( value, 0, 255 ) );
6853 else if ( part.compare( "hue"_L1, Qt::CaseInsensitive ) == 0 )
6854 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6855 else if ( part.compare( "saturation"_L1, Qt::CaseInsensitive ) == 0 )
6856 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6857 else if ( part.compare( "value"_L1, Qt::CaseInsensitive ) == 0 )
6858 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6859 else if ( part.compare( "hsl_hue"_L1, Qt::CaseInsensitive ) == 0 )
6860 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6861 else if ( part.compare( "hsl_saturation"_L1, Qt::CaseInsensitive ) == 0 )
6862 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6863 else if ( part.compare( "lightness"_L1, Qt::CaseInsensitive ) == 0 )
6864 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6865 else if ( part.compare( "cyan"_L1, Qt::CaseInsensitive ) == 0 )
6866 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6867 else if ( part.compare( "magenta"_L1, Qt::CaseInsensitive ) == 0 )
6868 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6869 else if ( part.compare( "yellow"_L1, Qt::CaseInsensitive ) == 0 )
6870 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6871 else if ( part.compare( "black"_L1, Qt::CaseInsensitive ) == 0 )
6872 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6873 else
6874 {
6875 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6876 return QVariant();
6877 }
6878 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6879}
6880
6881static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6882{
6883 const QVariant variant = values.at( 0 );
6884 bool isQColor;
6885 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6886 if ( !color.isValid() )
6887 return QVariant();
6888
6889 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6890
6891 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6892}
6893
6894static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6895{
6896 const QVariant variant = values.at( 0 );
6897 bool isQColor;
6898 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6899 if ( !color.isValid() )
6900 return QVariant();
6901
6902 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6903
6904 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6905}
6906
6907static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6908{
6909 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6910 QgsGeometry geom = feat.geometry();
6911 if ( !geom.isNull() )
6912 return QVariant::fromValue( geom );
6913 return QVariant();
6914}
6915
6916static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6917{
6918 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6919 if ( !feat.isValid() )
6920 return QVariant();
6921 return feat.id();
6922}
6923
6924static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6925{
6926 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6927 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
6928 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6929
6930 if ( !sCrs.isValid() )
6931 return QVariant::fromValue( fGeom );
6932
6933 if ( !dCrs.isValid() )
6934 return QVariant::fromValue( fGeom );
6935
6937 if ( context )
6938 tContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
6939 QgsCoordinateTransform t( sCrs, dCrs, tContext );
6940 try
6941 {
6943 return QVariant::fromValue( fGeom );
6944 }
6945 catch ( QgsCsException &cse )
6946 {
6947 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6948 return QVariant();
6949 }
6950 return QVariant();
6951}
6952
6953
6954static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6955{
6956 bool foundLayer = false;
6957 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6958
6959 //no layer found
6960 if ( !featureSource || !foundLayer )
6961 {
6962 return QVariant();
6963 }
6964
6965 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6966
6968 req.setFilterFid( fid );
6969 req.setTimeout( 10000 );
6970 req.setRequestMayBeNested( true );
6971 if ( context )
6972 req.setFeedback( context->feedback() );
6973 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6974
6975 QgsFeature fet;
6976 QVariant result;
6977 if ( fIt.nextFeature( fet ) )
6978 result = QVariant::fromValue( fet );
6979
6980 return result;
6981}
6982
6983static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6984{
6985 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6986 bool foundLayer = false;
6987 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6988
6989 //no layer found
6990 if ( !featureSource || !foundLayer )
6991 {
6992 return QVariant();
6993 }
6995 QString cacheValueKey;
6996 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6997 {
6998 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6999
7000 QMap<QString, QVariant>::const_iterator i = attributeMap.constBegin();
7001 QString filterString;
7002 for ( ; i != attributeMap.constEnd(); ++i )
7003 {
7004 if ( !filterString.isEmpty() )
7005 {
7006 filterString.append( " AND " );
7007 }
7008 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
7009 }
7010 cacheValueKey = u"getfeature:%1:%2"_s.arg( featureSource->id(), filterString );
7011 if ( context && context->hasCachedValue( cacheValueKey ) )
7012 {
7013 return context->cachedValue( cacheValueKey );
7014 }
7015 req.setFilterExpression( filterString );
7016 }
7017 else
7018 {
7019 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7020 int attributeId = featureSource->fields().lookupField( attribute );
7021 if ( attributeId == -1 )
7022 {
7023 return QVariant();
7024 }
7025
7026 const QVariant &attVal = values.at( 2 );
7027
7028 cacheValueKey = u"getfeature:%1:%2:%3"_s.arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
7029 if ( context && context->hasCachedValue( cacheValueKey ) )
7030 {
7031 return context->cachedValue( cacheValueKey );
7032 }
7033
7035 }
7036 req.setLimit( 1 );
7037 req.setTimeout( 10000 );
7038 req.setRequestMayBeNested( true );
7039 if ( context )
7040 req.setFeedback( context->feedback() );
7041 if ( !parent->needsGeometry() )
7042 {
7044 }
7045 QgsFeatureIterator fIt = featureSource->getFeatures( req );
7046
7047 QgsFeature fet;
7048 QVariant res;
7049 if ( fIt.nextFeature( fet ) )
7050 {
7051 res = QVariant::fromValue( fet );
7052 }
7053
7054 if ( context )
7055 context->setCachedValue( cacheValueKey, res );
7056 return res;
7057}
7058
7059static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7060{
7061 QVariant result;
7062 QString fieldName;
7063
7064 if ( context )
7065 {
7066 if ( !values.isEmpty() )
7067 {
7068 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
7069 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
7070 fieldName = col->name();
7071 else if ( values.size() == 2 )
7072 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7073 }
7074
7075 QVariant value = values.at( 0 );
7076
7077 const QgsFields fields = context->fields();
7078 int fieldIndex = fields.lookupField( fieldName );
7079
7080 if ( fieldIndex == -1 )
7081 {
7082 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( u"represent_value"_s, fieldName ) );
7083 }
7084 else
7085 {
7086 // TODO this function is NOT thread safe
7088 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( u"layer"_s ), context, parent );
7090
7091 const QString cacheValueKey = u"repvalfcnval:%1:%2:%3"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName, value.toString() );
7092 if ( context->hasCachedValue( cacheValueKey ) )
7093 {
7094 return context->cachedValue( cacheValueKey );
7095 }
7096
7097 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
7099
7100 const QString cacheKey = u"repvalfcn:%1:%2"_s.arg( layer ? layer->id() : u"[None]"_s, fieldName );
7101
7102 QVariant cache;
7103 if ( !context->hasCachedValue( cacheKey ) )
7104 {
7105 cache = formatter->createCache( layer, fieldIndex, setup.config() );
7106 context->setCachedValue( cacheKey, cache );
7107 }
7108 else
7109 cache = context->cachedValue( cacheKey );
7110
7111 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
7112
7113 context->setCachedValue( cacheValueKey, result );
7114 }
7115 }
7116 else
7117 {
7118 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( u"represent_value"_s, fieldName ) );
7119 }
7120
7121 return result;
7122}
7123
7124static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7125{
7126 const QVariant data = values.at( 0 );
7127 const QMimeDatabase db;
7128 return db.mimeTypeForData( data.toByteArray() ).name();
7129}
7130
7131static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7132{
7133 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7134
7135 bool foundLayer = false;
7136 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7137 values.at( 0 ),
7138 context,
7139 parent,
7140 [layerProperty]( QgsMapLayer *layer ) -> QVariant {
7141 if ( !layer )
7142 return QVariant();
7143
7144 // here, we always prefer the layer metadata values over the older server-specific published values
7145 if ( QString::compare( layerProperty, u"name"_s, Qt::CaseInsensitive ) == 0 )
7146 return layer->name();
7147 else if ( QString::compare( layerProperty, u"id"_s, Qt::CaseInsensitive ) == 0 )
7148 return layer->id();
7149 else if ( QString::compare( layerProperty, u"title"_s, Qt::CaseInsensitive ) == 0 )
7150 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
7151 else if ( QString::compare( layerProperty, u"abstract"_s, Qt::CaseInsensitive ) == 0 )
7152 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
7153 else if ( QString::compare( layerProperty, u"keywords"_s, Qt::CaseInsensitive ) == 0 )
7154 {
7155 QStringList keywords;
7156 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
7157 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
7158 {
7159 keywords.append( it.value() );
7160 }
7161 if ( !keywords.isEmpty() )
7162 return keywords;
7163 return layer->serverProperties()->keywordList();
7164 }
7165 else if ( QString::compare( layerProperty, u"data_url"_s, Qt::CaseInsensitive ) == 0 )
7166 return layer->serverProperties()->dataUrl();
7167 else if ( QString::compare( layerProperty, u"attribution"_s, Qt::CaseInsensitive ) == 0 )
7168 {
7169 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
7170 }
7171 else if ( QString::compare( layerProperty, u"attribution_url"_s, Qt::CaseInsensitive ) == 0 )
7172 return layer->serverProperties()->attributionUrl();
7173 else if ( QString::compare( layerProperty, u"source"_s, Qt::CaseInsensitive ) == 0 )
7174 return layer->publicSource();
7175 else if ( QString::compare( layerProperty, u"min_scale"_s, Qt::CaseInsensitive ) == 0 )
7176 return layer->minimumScale();
7177 else if ( QString::compare( layerProperty, u"max_scale"_s, Qt::CaseInsensitive ) == 0 )
7178 return layer->maximumScale();
7179 else if ( QString::compare( layerProperty, u"is_editable"_s, Qt::CaseInsensitive ) == 0 )
7180 return layer->isEditable();
7181 else if ( QString::compare( layerProperty, u"crs"_s, Qt::CaseInsensitive ) == 0 )
7182 return layer->crs().authid();
7183 else if ( QString::compare( layerProperty, u"crs_definition"_s, Qt::CaseInsensitive ) == 0 )
7184 return layer->crs().toProj();
7185 else if ( QString::compare( layerProperty, u"crs_description"_s, Qt::CaseInsensitive ) == 0 )
7186 return layer->crs().description();
7187 else if ( QString::compare( layerProperty, u"crs_ellipsoid"_s, Qt::CaseInsensitive ) == 0 )
7188 return layer->crs().ellipsoidAcronym();
7189 else if ( QString::compare( layerProperty, u"extent"_s, Qt::CaseInsensitive ) == 0 )
7190 {
7191 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
7192 QVariant result = QVariant::fromValue( extentGeom );
7193 return result;
7194 }
7195 else if ( QString::compare( layerProperty, u"distance_units"_s, Qt::CaseInsensitive ) == 0 )
7196 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
7197 else if ( QString::compare( layerProperty, u"path"_s, Qt::CaseInsensitive ) == 0 )
7198 {
7199 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
7200 return decodedUri.value( u"path"_s );
7201 }
7202 else if ( QString::compare( layerProperty, u"type"_s, Qt::CaseInsensitive ) == 0 )
7203 {
7204 switch ( layer->type() )
7205 {
7207 return QCoreApplication::translate( "expressions", "Vector" );
7209 return QCoreApplication::translate( "expressions", "Raster" );
7211 return QCoreApplication::translate( "expressions", "Mesh" );
7213 return QCoreApplication::translate( "expressions", "Vector Tile" );
7215 return QCoreApplication::translate( "expressions", "Plugin" );
7217 return QCoreApplication::translate( "expressions", "Annotation" );
7219 return QCoreApplication::translate( "expressions", "Point Cloud" );
7221 return QCoreApplication::translate( "expressions", "Group" );
7223 return QCoreApplication::translate( "expressions", "Tiled Scene" );
7224 }
7225 }
7226 else
7227 {
7228 //vector layer methods
7229 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
7230 if ( vLayer )
7231 {
7232 if ( QString::compare( layerProperty, u"storage_type"_s, Qt::CaseInsensitive ) == 0 )
7233 return vLayer->storageType();
7234 else if ( QString::compare( layerProperty, u"geometry_type"_s, Qt::CaseInsensitive ) == 0 )
7236 else if ( QString::compare( layerProperty, u"feature_count"_s, Qt::CaseInsensitive ) == 0 )
7237 return QVariant::fromValue( vLayer->featureCount() );
7238 }
7239 }
7240
7241 return QVariant();
7242 },
7243 foundLayer
7244 );
7245
7246 if ( !foundLayer )
7247 return QVariant();
7248 else
7249 return res;
7250}
7251
7252static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7253{
7254 const QString uriPart = values.at( 1 ).toString();
7255
7256 bool foundLayer = false;
7257
7258 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7259 values.at( 0 ),
7260 context,
7261 parent,
7262 [parent, uriPart]( QgsMapLayer *layer ) -> QVariant {
7263 if ( !layer->dataProvider() )
7264 {
7265 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7266 return QVariant();
7267 }
7268
7269 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7270
7271 if ( !uriPart.isNull() )
7272 {
7273 return decodedUri.value( uriPart );
7274 }
7275 else
7276 {
7277 return decodedUri;
7278 }
7279 },
7280 foundLayer
7281 );
7282
7283 if ( !foundLayer )
7284 {
7285 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7286 return QVariant();
7287 }
7288 else
7289 {
7290 return res;
7291 }
7292}
7293
7294static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7295{
7296 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7297 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7298
7299 bool foundLayer = false;
7300 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe(
7301 values.at( 0 ),
7302 context,
7303 parent,
7304 [parent, band, layerProperty]( QgsMapLayer *layer ) -> QVariant {
7305 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7306 if ( !rl )
7307 return QVariant();
7308
7309 if ( band < 1 || band > rl->bandCount() )
7310 {
7311 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7312 return QVariant();
7313 }
7314
7316
7317 if ( QString::compare( layerProperty, u"avg"_s, Qt::CaseInsensitive ) == 0 )
7319 else if ( QString::compare( layerProperty, u"stdev"_s, Qt::CaseInsensitive ) == 0 )
7321 else if ( QString::compare( layerProperty, u"min"_s, Qt::CaseInsensitive ) == 0 )
7323 else if ( QString::compare( layerProperty, u"max"_s, Qt::CaseInsensitive ) == 0 )
7325 else if ( QString::compare( layerProperty, u"range"_s, Qt::CaseInsensitive ) == 0 )
7327 else if ( QString::compare( layerProperty, u"sum"_s, Qt::CaseInsensitive ) == 0 )
7329 else
7330 {
7331 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7332 return QVariant();
7333 }
7334
7335 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7336 switch ( stat )
7337 {
7339 return stats.mean;
7341 return stats.stdDev;
7343 return stats.minimumValue;
7345 return stats.maximumValue;
7347 return stats.range;
7349 return stats.sum;
7350 default:
7351 break;
7352 }
7353 return QVariant();
7354 },
7355 foundLayer
7356 );
7357
7358 if ( !foundLayer )
7359 {
7360#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7361 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7362#endif
7363 return QVariant();
7364 }
7365 else
7366 {
7367 return res;
7368 }
7369}
7370
7371static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7372{
7373 return values;
7374}
7375
7376static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7377{
7378 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7379 bool ascending = values.value( 1 ).toBool();
7380 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7381 return list;
7382}
7383
7384static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7385{
7386 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7387}
7388
7389static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7390{
7391 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7392}
7393
7394static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7395{
7396 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7397}
7398
7399static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7400{
7401 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7402 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7403 int match = 0;
7404 for ( const auto &item : listB )
7405 {
7406 if ( listA.contains( item ) )
7407 match++;
7408 }
7409
7410 return QVariant( match == listB.count() );
7411}
7412
7413static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7414{
7415 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7416}
7417
7418static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7419{
7420 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7421 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7422 if ( pos < list.length() && pos >= 0 )
7423 return list.at( pos );
7424 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7425 return list.at( list.length() + pos );
7426 return QVariant();
7427}
7428
7429static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7430{
7431 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7432 return list.value( 0 );
7433}
7434
7435static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7436{
7437 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7438 return list.value( list.size() - 1 );
7439}
7440
7441static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7442{
7443 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7444 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7445}
7446
7447static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7448{
7449 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7450 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7451}
7452
7453static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7454{
7455 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7456 int i = 0;
7457 double total = 0.0;
7458 for ( const QVariant &item : list )
7459 {
7460 switch ( item.userType() )
7461 {
7462 case QMetaType::Int:
7463 case QMetaType::UInt:
7464 case QMetaType::LongLong:
7465 case QMetaType::ULongLong:
7466 case QMetaType::Float:
7467 case QMetaType::Double:
7468 total += item.toDouble();
7469 ++i;
7470 break;
7471 }
7472 }
7473 return i == 0 ? QVariant() : total / i;
7474}
7475
7476static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7477{
7478 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7479 QVariantList numbers;
7480 for ( const auto &item : list )
7481 {
7482 switch ( item.userType() )
7483 {
7484 case QMetaType::Int:
7485 case QMetaType::UInt:
7486 case QMetaType::LongLong:
7487 case QMetaType::ULongLong:
7488 case QMetaType::Float:
7489 case QMetaType::Double:
7490 numbers.append( item );
7491 break;
7492 }
7493 }
7494 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7495 const int count = numbers.count();
7496 if ( count == 0 )
7497 {
7498 return QVariant();
7499 }
7500 else if ( count % 2 )
7501 {
7502 return numbers.at( count / 2 );
7503 }
7504 else
7505 {
7506 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7507 }
7508}
7509
7510static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7511{
7512 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7513 int i = 0;
7514 double total = 0.0;
7515 for ( const QVariant &item : list )
7516 {
7517 switch ( item.userType() )
7518 {
7519 case QMetaType::Int:
7520 case QMetaType::UInt:
7521 case QMetaType::LongLong:
7522 case QMetaType::ULongLong:
7523 case QMetaType::Float:
7524 case QMetaType::Double:
7525 total += item.toDouble();
7526 ++i;
7527 break;
7528 }
7529 }
7530 return i == 0 ? QVariant() : total;
7531}
7532
7533static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7534{
7535 QVariant result = value;
7536 ( void ) result.convert( static_cast<int>( type ) );
7537 return result;
7538}
7539
7540static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7541{
7542 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7543 QHash< QVariant, int > hash;
7544 for ( const auto &item : list )
7545 {
7546 ++hash[item];
7547 }
7548 const QList< int > occurrences = hash.values();
7549 if ( occurrences.empty() )
7550 return QVariantList();
7551
7552 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7553
7554 const QString option = values.at( 1 ).toString();
7555 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7556 {
7557 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7558 }
7559 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7560 {
7561 if ( hash.isEmpty() )
7562 return QVariant();
7563
7564 return QVariant( hash.key( maxValue ) );
7565 }
7566 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7567 {
7568 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7569 }
7570 else if ( option.compare( "real_majority"_L1, Qt::CaseInsensitive ) == 0 )
7571 {
7572 if ( maxValue * 2 <= list.size() )
7573 return QVariant();
7574
7575 return QVariant( hash.key( maxValue ) );
7576 }
7577 else
7578 {
7579 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7580 return QVariant();
7581 }
7582}
7583
7584static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7585{
7586 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7587 QHash< QVariant, int > hash;
7588 for ( const auto &item : list )
7589 {
7590 ++hash[item];
7591 }
7592 const QList< int > occurrences = hash.values();
7593 if ( occurrences.empty() )
7594 return QVariantList();
7595
7596 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7597
7598 const QString option = values.at( 1 ).toString();
7599 if ( option.compare( "all"_L1, Qt::CaseInsensitive ) == 0 )
7600 {
7601 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7602 }
7603 else if ( option.compare( "any"_L1, Qt::CaseInsensitive ) == 0 )
7604 {
7605 if ( hash.isEmpty() )
7606 return QVariant();
7607
7608 return QVariant( hash.key( minValue ) );
7609 }
7610 else if ( option.compare( "median"_L1, Qt::CaseInsensitive ) == 0 )
7611 {
7612 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7613 }
7614 else if ( option.compare( "real_minority"_L1, Qt::CaseInsensitive ) == 0 )
7615 {
7616 if ( hash.isEmpty() )
7617 return QVariant();
7618
7619 // Remove the majority, all others are minority
7620 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7621 if ( maxValue * 2 > list.size() )
7622 hash.remove( hash.key( maxValue ) );
7623
7624 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7625 }
7626 else
7627 {
7628 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7629 return QVariant();
7630 }
7631}
7632
7633static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7634{
7635 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7636 list.append( values.at( 1 ) );
7637 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7638}
7639
7640static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7641{
7642 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7643 list.prepend( values.at( 1 ) );
7644 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7645}
7646
7647static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7648{
7649 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7650 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7651 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7652}
7653
7654static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7655{
7656 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7657 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7658 if ( position < 0 )
7659 position = position + list.length();
7660 if ( position >= 0 && position < list.length() )
7661 list.removeAt( position );
7662 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7663}
7664
7665static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7666{
7667 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7668 return QVariant();
7669
7670 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7671
7672 const QVariant toRemove = values.at( 1 );
7673 if ( QgsVariantUtils::isNull( toRemove ) )
7674 {
7675 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant &element ) { return QgsVariantUtils::isNull( element ); } ), list.end() );
7676 }
7677 else
7678 {
7679 list.removeAll( toRemove );
7680 }
7681 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7682}
7683
7684static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7685{
7686 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7687 {
7688 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7689
7690 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7691 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7692 {
7693 int index = list.indexOf( it.key() );
7694 while ( index >= 0 )
7695 {
7696 list.replace( index, it.value() );
7697 index = list.indexOf( it.key() );
7698 }
7699 }
7700
7701 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7702 }
7703 else if ( values.count() == 3 )
7704 {
7705 QVariantList before;
7706 QVariantList after;
7707 bool isSingleReplacement = false;
7708
7709 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7710 {
7711 before = QVariantList() << values.at( 1 );
7712 }
7713 else
7714 {
7715 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7716 }
7717
7718 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7719 {
7720 after = QVariantList() << values.at( 2 );
7721 isSingleReplacement = true;
7722 }
7723 else
7724 {
7725 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7726 }
7727
7728 if ( !isSingleReplacement && before.length() != after.length() )
7729 {
7730 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7731 return QVariant();
7732 }
7733
7734 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7735 for ( int i = 0; i < before.length(); i++ )
7736 {
7737 int index = list.indexOf( before.at( i ) );
7738 while ( index >= 0 )
7739 {
7740 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7741 index = list.indexOf( before.at( i ) );
7742 }
7743 }
7744
7745 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7746 }
7747 else
7748 {
7749 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7750 return QVariant();
7751 }
7752}
7753
7754static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7755{
7756 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7757 QVariantList list_new;
7758
7759 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7760 {
7761 while ( list.removeOne( cur ) )
7762 {
7763 list_new.append( cur );
7764 }
7765 }
7766
7767 list_new.append( list );
7768
7769 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7770}
7771
7772static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7773{
7774 QVariantList list;
7775 for ( const QVariant &cur : values )
7776 {
7777 list += QgsExpressionUtils::getListValue( cur, parent );
7778 }
7779 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7780}
7781
7782static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7783{
7784 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7785 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7786 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7787 int slice_length = 0;
7788 // negative positions means positions taken relative to the end of the array
7789 if ( start_pos < 0 )
7790 {
7791 start_pos = list.length() + start_pos;
7792 }
7793 if ( end_pos >= 0 )
7794 {
7795 slice_length = end_pos - start_pos + 1;
7796 }
7797 else
7798 {
7799 slice_length = list.length() + end_pos - start_pos + 1;
7800 }
7801 //avoid negative lengths in QList.mid function
7802 if ( slice_length < 0 )
7803 {
7804 slice_length = 0;
7805 }
7806 list = list.mid( start_pos, slice_length );
7807 return list;
7808}
7809
7810static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7811{
7812 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7813 std::reverse( list.begin(), list.end() );
7814 return list;
7815}
7816
7817static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7818{
7819 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7820 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7821 for ( const QVariant &cur : array2 )
7822 {
7823 if ( array1.contains( cur ) )
7824 return QVariant( true );
7825 }
7826 return QVariant( false );
7827}
7828
7829static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7830{
7831 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7832
7833 QVariantList distinct;
7834
7835 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7836 {
7837 if ( !distinct.contains( *it ) )
7838 {
7839 distinct += ( *it );
7840 }
7841 }
7842
7843 return distinct;
7844}
7845
7846static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7847{
7848 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7849 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7850 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7851
7852 QString str;
7853
7854 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7855 {
7856 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7857 if ( it != ( array.constEnd() - 1 ) )
7858 {
7859 str += delimiter;
7860 }
7861 }
7862
7863 return QVariant( str );
7864}
7865
7866static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7867{
7868 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7869 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7870 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7871
7872 QStringList list = str.split( delimiter );
7873 QVariantList array;
7874
7875 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7876 {
7877 array += ( !( *it ).isEmpty() ) ? *it : empty;
7878 }
7879
7880 return array;
7881}
7882
7883static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7884{
7885 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7886 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7887 if ( document.isNull() )
7888 return QVariant();
7889
7890 return document.toVariant();
7891}
7892
7893static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7894{
7895 Q_UNUSED( parent )
7896 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7897 return QString( document.toJson( QJsonDocument::Compact ) );
7898}
7899
7900static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7901{
7902 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7903 if ( str.isEmpty() )
7904 return QVariantMap();
7905 str = str.trimmed();
7906
7907 return QgsHstoreUtils::parse( str );
7908}
7909
7910static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7911{
7912 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7913 return QgsHstoreUtils::build( map );
7914}
7915
7916static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7917{
7918 QVariantMap result;
7919 for ( int i = 0; i + 1 < values.length(); i += 2 )
7920 {
7921 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7922 }
7923 return result;
7924}
7925
7926static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7927{
7928 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7929 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7930 QVariantMap resultMap;
7931
7932 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7933 {
7934 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7935 }
7936
7937 return resultMap;
7938}
7939
7940static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7941{
7942 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7943}
7944
7945static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7946{
7947 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7948}
7949
7950static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7951{
7952 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7953 map.remove( values.at( 1 ).toString() );
7954 return map;
7955}
7956
7957static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7958{
7959 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7960 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7961 return map;
7962}
7963
7964static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7965{
7966 QVariantMap result;
7967 for ( const QVariant &cur : values )
7968 {
7969 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7970 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7971 result.insert( it.key(), it.value() );
7972 }
7973 return result;
7974}
7975
7976static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7977{
7978 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7979}
7980
7981static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7982{
7983 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7984}
7985
7986static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7987{
7988 const QString envVarName = values.at( 0 ).toString();
7989 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7990 return QVariant();
7991
7992 return QProcessEnvironment::systemEnvironment().value( envVarName );
7993}
7994
7995static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7996{
7997 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7998 if ( parent->hasEvalError() )
7999 {
8000 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "base_file_name"_L1 ) );
8001 return QVariant();
8002 }
8003 return QFileInfo( file ).completeBaseName();
8004}
8005
8006static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8007{
8008 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8009 if ( parent->hasEvalError() )
8010 {
8011 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_suffix"_L1 ) );
8012 return QVariant();
8013 }
8014 return QFileInfo( file ).completeSuffix();
8015}
8016
8017static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8018{
8019 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8020 if ( parent->hasEvalError() )
8021 {
8022 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_exists"_L1 ) );
8023 return QVariant();
8024 }
8025 return QFileInfo::exists( file );
8026}
8027
8028static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8029{
8030 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8031 if ( parent->hasEvalError() )
8032 {
8033 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_name"_L1 ) );
8034 return QVariant();
8035 }
8036 return QFileInfo( file ).fileName();
8037}
8038
8039static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8040{
8041 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8042 if ( parent->hasEvalError() )
8043 {
8044 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_file"_L1 ) );
8045 return QVariant();
8046 }
8047 return QFileInfo( file ).isFile();
8048}
8049
8050static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8051{
8052 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8053 if ( parent->hasEvalError() )
8054 {
8055 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "is_directory"_L1 ) );
8056 return QVariant();
8057 }
8058 return QFileInfo( file ).isDir();
8059}
8060
8061static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8062{
8063 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8064 if ( parent->hasEvalError() )
8065 {
8066 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_path"_L1 ) );
8067 return QVariant();
8068 }
8069 return QDir::toNativeSeparators( QFileInfo( file ).path() );
8070}
8071
8072static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8073{
8074 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
8075 if ( parent->hasEvalError() )
8076 {
8077 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( "file_size"_L1 ) );
8078 return QVariant();
8079 }
8080 return QFileInfo( file ).size();
8081}
8082
8083static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
8084{
8085 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
8086}
8087
8088static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8089{
8090 QVariant hash;
8091 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8092 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
8093
8094 if ( method == "md4"_L1 )
8095 {
8096 hash = fcnHash( str, QCryptographicHash::Md4 );
8097 }
8098 else if ( method == "md5"_L1 )
8099 {
8100 hash = fcnHash( str, QCryptographicHash::Md5 );
8101 }
8102 else if ( method == "sha1"_L1 )
8103 {
8104 hash = fcnHash( str, QCryptographicHash::Sha1 );
8105 }
8106 else if ( method == "sha224"_L1 )
8107 {
8108 hash = fcnHash( str, QCryptographicHash::Sha224 );
8109 }
8110 else if ( method == "sha256"_L1 )
8111 {
8112 hash = fcnHash( str, QCryptographicHash::Sha256 );
8113 }
8114 else if ( method == "sha384"_L1 )
8115 {
8116 hash = fcnHash( str, QCryptographicHash::Sha384 );
8117 }
8118 else if ( method == "sha512"_L1 )
8119 {
8120 hash = fcnHash( str, QCryptographicHash::Sha512 );
8121 }
8122 else if ( method == "sha3_224"_L1 )
8123 {
8124 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
8125 }
8126 else if ( method == "sha3_256"_L1 )
8127 {
8128 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
8129 }
8130 else if ( method == "sha3_384"_L1 )
8131 {
8132 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
8133 }
8134 else if ( method == "sha3_512"_L1 )
8135 {
8136 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
8137 }
8138 else if ( method == "keccak_224"_L1 )
8139 {
8140 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
8141 }
8142 else if ( method == "keccak_256"_L1 )
8143 {
8144 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
8145 }
8146 else if ( method == "keccak_384"_L1 )
8147 {
8148 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
8149 }
8150 else if ( method == "keccak_512"_L1 )
8151 {
8152 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
8153 }
8154 else
8155 {
8156 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
8157 }
8158 return hash;
8159}
8160
8161static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8162{
8163 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
8164}
8165
8166static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8167{
8168 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
8169}
8170
8171static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
8172{
8173 const QByteArray input = values.at( 0 ).toByteArray();
8174 return QVariant( QString( input.toBase64() ) );
8175}
8176
8177static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8178{
8179 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
8180 QUrlQuery query;
8181 for ( auto it = map.cbegin(); it != map.cend(); it++ )
8182 {
8183 query.addQueryItem( it.key(), it.value().toString() );
8184 }
8185 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
8186}
8187
8188static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
8189{
8190 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
8191 const QByteArray base64 = value.toLocal8Bit();
8192 const QByteArray decoded = QByteArray::fromBase64( base64 );
8193 return QVariant( decoded );
8194}
8195
8196typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
8197
8198static QVariant executeGeomOverlay(
8199 const QVariantList &values,
8200 const QgsExpressionContext *context,
8201 QgsExpression *parent,
8202 const RelationFunction &relationFunction,
8203 bool invert = false,
8204 double bboxGrow = 0,
8205 bool isNearestFunc = false,
8206 bool isIntersectsFunc = false
8207)
8208{
8209 if ( !context )
8210 {
8211 parent->setEvalErrorString( u"This function was called without an expression context."_s );
8212 return QVariant();
8213 }
8214
8215 const QVariant sourceLayerRef = context->variable( u"layer"_s ); //used to detect if sourceLayer and targetLayer are the same
8216 // TODO this function is NOT thread safe
8218 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
8220
8221 QgsFeatureRequest request;
8222 request.setTimeout( 10000 );
8223 request.setRequestMayBeNested( true );
8224 request.setFeedback( context->feedback() );
8225
8226 // First parameter is the overlay layer
8227 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
8229
8230 const bool layerCanBeCached = node->isStatic( parent, context );
8231 QVariant targetLayerValue = node->eval( parent, context );
8233
8234 // Second parameter is the expression to evaluate (or null for testonly)
8235 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
8237 QString subExpString = node->dump();
8238
8239 bool testOnly = ( subExpString == "NULL" );
8240 // TODO this function is NOT thread safe
8242 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
8244 if ( !targetLayer ) // No layer, no joy
8245 {
8246 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
8247 return QVariant();
8248 }
8249
8250 // Third parameter is the filtering expression
8251 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
8253 QString filterString = node->dump();
8254 if ( filterString != "NULL" )
8255 {
8256 request.setFilterExpression( filterString ); //filter cached features
8257 }
8258
8259 // Fourth parameter is the limit
8260 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8262 QVariant limitValue = node->eval( parent, context );
8264 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
8265
8266 // Fifth parameter (for nearest only) is the max distance
8267 double max_distance = 0;
8268 if ( isNearestFunc ) //maxdistance param handling
8269 {
8270 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
8272 QVariant distanceValue = node->eval( parent, context );
8274 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8275 }
8276
8277 // Fifth or sixth (for nearest only) parameter is the cache toggle
8278 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
8280 QVariant cacheValue = node->eval( parent, context );
8282 bool cacheEnabled = cacheValue.toBool();
8283
8284 // Sixth parameter (for intersects only) is the min overlap (area or length)
8285 // Seventh parameter (for intersects only) is the min inscribed circle radius
8286 // Eighth parameter (for intersects only) is the return_details
8287 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
8288 double minOverlap { -1 };
8289 double minInscribedCircleRadius { -1 };
8290 bool returnDetails = false; //#spellok
8291 bool sortByMeasure = false;
8292 bool sortAscending = false;
8293 bool requireMeasures = false;
8294 bool overlapOrRadiusFilter = false;
8295 if ( isIntersectsFunc )
8296 {
8297 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8299 const QVariant minOverlapValue = node->eval( parent, context );
8301 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8302 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8304 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8306 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8307 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8308 // Return measures is only effective when an expression is set
8309 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8310 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8311 // Sort by measures is only effective when an expression is set
8312 const QString sorting { node->eval( parent, context ).toString().toLower() };
8313 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8314 sortAscending = sorting.startsWith( "asc" );
8315 requireMeasures = sortByMeasure || returnDetails; //#spellok
8316 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8317 }
8318
8319
8320 FEAT_FROM_CONTEXT( context, feat )
8321 const QgsGeometry geometry = feat.geometry();
8322
8323 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8324 {
8325 QgsCoordinateTransformContext TransformContext = context->variable( u"_project_transform_context"_s ).value<QgsCoordinateTransformContext>();
8326 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8327 }
8328
8329 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8330
8331 QgsRectangle intDomain = geometry.boundingBox();
8332 if ( bboxGrow != 0 )
8333 {
8334 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8335 }
8336
8337 const QString cacheBase { u"%1:%2:%3"_s.arg( targetLayer->id(), subExpString, filterString ) };
8338
8339 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8340 // Otherwise, it can be toggled by the user
8341 QgsSpatialIndex spatialIndex;
8342 QgsVectorLayer *cachedTarget;
8343 QList<QgsFeature> features;
8344 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8345 {
8346 // If the cache (local spatial index) is enabled, we materialize the whole
8347 // layer, then do the request on that layer instead.
8348 const QString cacheLayer { u"ovrlaylyr:%1"_s.arg( cacheBase ) };
8349 const QString cacheIndex { u"ovrlayidx:%1"_s.arg( cacheBase ) };
8350
8351 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8352 {
8353 cachedTarget = targetLayer->materialize( request );
8354 if ( layerCanBeCached )
8355 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8356 }
8357 else
8358 {
8359 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8360 }
8361
8362 if ( !context->hasCachedValue( cacheIndex ) )
8363 {
8364 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8365 if ( layerCanBeCached )
8366 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8367 }
8368 else
8369 {
8370 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8371 }
8372
8373 QList<QgsFeatureId> fidsList;
8374 if ( isNearestFunc )
8375 {
8376 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8377 }
8378 else
8379 {
8380 fidsList = spatialIndex.intersects( intDomain );
8381 }
8382
8383 QListIterator<QgsFeatureId> i( fidsList );
8384 while ( i.hasNext() )
8385 {
8386 QgsFeatureId fId2 = i.next();
8387 if ( sameLayers && feat.id() == fId2 )
8388 continue;
8389 features.append( cachedTarget->getFeature( fId2 ) );
8390 }
8391 }
8392 else
8393 {
8394 // If the cache (local spatial index) is not enabled, we directly
8395 // get the features from the target layer
8396 request.setFilterRect( intDomain );
8397 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8398 QgsFeature feat2;
8399 while ( fit.nextFeature( feat2 ) )
8400 {
8401 if ( sameLayers && feat.id() == feat2.id() )
8402 continue;
8403 features.append( feat2 );
8404 }
8405 }
8406
8407 QgsExpression subExpression;
8408 QgsExpressionContext subContext;
8409 if ( !testOnly )
8410 {
8411 const QString expCacheKey { u"exp:%1"_s.arg( cacheBase ) };
8412 const QString ctxCacheKey { u"ctx:%1"_s.arg( cacheBase ) };
8413
8414 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8415 {
8416 subExpression = QgsExpression( subExpString );
8418 subExpression.prepare( &subContext );
8419 }
8420 else
8421 {
8422 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8423 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8424 }
8425 }
8426
8427 // //////////////////////////////////////////////////////////////////
8428 // Helper functions for geometry tests
8429
8430 // Test function for linestring geometries, returns TRUE if test passes
8431 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double &overlapValue ) -> bool {
8432 bool testResult { false };
8433 // For return measures:
8434 QVector<double> overlapValues;
8435 const QgsGeometry merged { intersection.mergeLines() };
8436 for ( auto it = merged.const_parts_begin(); !testResult && it != merged.const_parts_end(); ++it )
8437 {
8439 // Check min overlap for intersection (if set)
8440 if ( minOverlap != -1 || requireMeasures )
8441 {
8442 overlapValue = geom->length();
8443 overlapValues.append( overlapValue );
8444 if ( minOverlap != -1 )
8445 {
8446 if ( overlapValue >= minOverlap )
8447 {
8448 testResult = true;
8449 }
8450 else
8451 {
8452 continue;
8453 }
8454 }
8455 }
8456 }
8457
8458 if ( !overlapValues.isEmpty() )
8459 {
8460 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8461 }
8462
8463 return testResult;
8464 };
8465
8466 // Test function for polygon geometries, returns TRUE if test passes
8467 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double &radiusValue, double &overlapValue ) -> bool {
8468 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8469 bool testResult { false };
8470 // For return measures:
8471 QVector<double> overlapValues;
8472 QVector<double> radiusValues;
8473 for ( auto it = intersection.const_parts_begin(); ( !testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8474 {
8476 // Check min overlap for intersection (if set)
8477 if ( minOverlap != -1 || requireMeasures )
8478 {
8479 overlapValue = geom->area();
8480 overlapValues.append( geom->area() );
8481 if ( minOverlap != -1 )
8482 {
8483 if ( overlapValue >= minOverlap )
8484 {
8485 testResult = true;
8486 }
8487 else
8488 {
8489 continue;
8490 }
8491 }
8492 }
8493
8494 // Check min inscribed circle radius for intersection (if set)
8495 if ( minInscribedCircleRadius != -1 || requireMeasures )
8496 {
8497 const QgsRectangle bbox = geom->boundingBox();
8498 const double width = bbox.width();
8499 const double height = bbox.height();
8500 const double size = width > height ? width : height;
8501 const double tolerance = size / 100.0;
8502 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8503 testResult = radiusValue >= minInscribedCircleRadius;
8504 radiusValues.append( radiusValues );
8505 }
8506 } // end for parts
8507
8508 // Get the max values
8509 if ( !radiusValues.isEmpty() )
8510 {
8511 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8512 }
8513
8514 if ( !overlapValues.isEmpty() )
8515 {
8516 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8517 }
8518
8519 return testResult;
8520 };
8521
8522
8523 bool found = false;
8524 int foundCount = 0;
8525 QVariantList results;
8526
8527 QListIterator<QgsFeature> i( features );
8528 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8529 {
8530 QgsFeature feat2 = i.next();
8531
8532
8533 if ( !relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8534 {
8535 double overlapValue = -1;
8536 double radiusValue = -1;
8537
8538 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8539 {
8540 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8541
8542 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8543 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8544 {
8545 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8546 intersection = QgsGeometry();
8547 QgsMultiPolygonXY poly;
8548 QgsMultiPolylineXY line;
8549 QgsMultiPointXY point;
8550 for ( const auto &geom : std::as_const( geometries ) )
8551 {
8552 switch ( geom.type() )
8553 {
8555 {
8556 poly.append( geom.asPolygon() );
8557 break;
8558 }
8560 {
8561 line.append( geom.asPolyline() );
8562 break;
8563 }
8565 {
8566 point.append( geom.asPoint() );
8567 break;
8568 }
8571 {
8572 break;
8573 }
8574 }
8575 }
8576
8577 switch ( geometry.type() )
8578 {
8580 {
8581 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8582 break;
8583 }
8585 {
8586 intersection = QgsGeometry::fromMultiPolylineXY( line );
8587 break;
8588 }
8590 {
8591 intersection = QgsGeometry::fromMultiPointXY( point );
8592 break;
8593 }
8596 {
8597 break;
8598 }
8599 }
8600 }
8601
8602 // Depending on the intersection geometry type and on the geometry type of
8603 // the tested geometry we can run different tests and collect different measures
8604 // that can be used for sorting (if required).
8605 switch ( intersection.type() )
8606 {
8608 {
8609 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8610 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8611
8612 if ( !testResult && overlapOrRadiusFilter )
8613 {
8614 continue;
8615 }
8616
8617 break;
8618 }
8619
8621 {
8622 // If the intersection is a linestring and a minimum circle is required
8623 // we can discard this result immediately.
8624 if ( minInscribedCircleRadius != -1 )
8625 {
8626 continue;
8627 }
8628
8629 // Otherwise a test for the overlap value is performed.
8630 const bool testResult { testLinestring( intersection, overlapValue ) };
8631
8632 if ( !testResult && overlapOrRadiusFilter )
8633 {
8634 continue;
8635 }
8636
8637 break;
8638 }
8639
8641 {
8642 // If the intersection is a point and a minimum circle is required
8643 // we can discard this result immediately.
8644 if ( minInscribedCircleRadius != -1 )
8645 {
8646 continue;
8647 }
8648
8649 bool testResult { false };
8650 if ( minOverlap != -1 || requireMeasures )
8651 {
8652 // Initially set this to 0 because it's a point intersection...
8653 overlapValue = 0;
8654 // ... but if the target geometry is not a point and the source
8655 // geometry is a point, we must record the length or the area
8656 // of the intersected geometry and use that as a measure for
8657 // sorting or reporting.
8658 if ( geometry.type() == Qgis::GeometryType::Point )
8659 {
8660 switch ( feat2.geometry().type() )
8661 {
8665 {
8666 break;
8667 }
8669 {
8670 testResult = testLinestring( feat2.geometry(), overlapValue );
8671 break;
8672 }
8674 {
8675 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8676 break;
8677 }
8678 }
8679 }
8680
8681 if ( !testResult && overlapOrRadiusFilter )
8682 {
8683 continue;
8684 }
8685 }
8686 break;
8687 }
8688
8691 {
8692 continue;
8693 }
8694 }
8695 }
8696
8697 found = true;
8698 foundCount++;
8699
8700 // We just want a single boolean result if there is any intersect: finish and return true
8701 if ( testOnly )
8702 break;
8703
8704 if ( !invert )
8705 {
8706 // We want a list of attributes / geometries / other expression values, evaluate now
8707 subContext.setFeature( feat2 );
8708 const QVariant expResult = subExpression.evaluate( &subContext );
8709
8710 if ( requireMeasures )
8711 {
8712 QVariantMap resultRecord;
8713 resultRecord.insert( u"id"_s, feat2.id() );
8714 resultRecord.insert( u"result"_s, expResult );
8715 // Overlap is always added because return measures was set
8716 resultRecord.insert( u"overlap"_s, overlapValue );
8717 // Radius is only added when is different than -1 (because for linestrings is not set)
8718 if ( radiusValue != -1 )
8719 {
8720 resultRecord.insert( u"radius"_s, radiusValue );
8721 }
8722 results.append( resultRecord );
8723 }
8724 else
8725 {
8726 results.append( expResult );
8727 }
8728 }
8729 else
8730 {
8731 // If not, results is a list of found ids, which we'll inverse and evaluate below
8732 results.append( feat2.id() );
8733 }
8734 }
8735 }
8736
8737 if ( testOnly )
8738 {
8739 if ( invert )
8740 found = !found; //for disjoint condition
8741 return found;
8742 }
8743
8744 if ( !invert )
8745 {
8746 if ( requireMeasures )
8747 {
8748 if ( sortByMeasure )
8749 {
8750 std::sort( results.begin(), results.end(), [sortAscending]( const QVariant &recordA, const QVariant &recordB ) -> bool {
8751 return sortAscending ? recordB.toMap().value( u"overlap"_s ).toDouble() > recordA.toMap().value( u"overlap"_s ).toDouble()
8752 : recordA.toMap().value( u"overlap"_s ).toDouble() > recordB.toMap().value( u"overlap"_s ).toDouble();
8753 } );
8754 }
8755 // Resize
8756 if ( limit > 0 && results.size() > limit )
8757 {
8758 results.erase( results.begin() + limit );
8759 }
8760
8761 if ( !returnDetails ) //#spellok
8762 {
8763 QVariantList expResults;
8764 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8765 {
8766 expResults.append( it->toMap().value( u"result"_s ) );
8767 }
8768 return expResults;
8769 }
8770 }
8771
8772 return results;
8773 }
8774
8775 // for disjoint condition returns the results for cached layers not intersected feats
8776 QVariantList disjoint_results;
8777 QgsFeature feat2;
8778 QgsFeatureRequest request2;
8779 request2.setLimit( limit );
8780 if ( context )
8781 request2.setFeedback( context->feedback() );
8782 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8783 while ( fi.nextFeature( feat2 ) )
8784 {
8785 if ( !results.contains( feat2.id() ) )
8786 {
8787 subContext.setFeature( feat2 );
8788 disjoint_results.append( subExpression.evaluate( &subContext ) );
8789 }
8790 }
8791 return disjoint_results;
8792}
8793
8794// Intersect functions:
8795
8796static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8797{
8798 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8799}
8800
8801static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8802{
8803 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8804}
8805
8806static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8807{
8808 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8809}
8810
8811static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8812{
8813 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8814}
8815
8816static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8817{
8818 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8819}
8820
8821static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8822{
8823 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8824}
8826static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8827{
8828 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8829}
8830
8831static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8832{
8833 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8834}
8835
8836const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8837{
8838 // The construction of the list isn't thread-safe, and without the mutex,
8839 // crashes in the WFS provider may occur, since it can parse expressions
8840 // in parallel.
8841 // The mutex needs to be recursive.
8842 QMutexLocker locker( &sFunctionsMutex );
8843
8844 QList<QgsExpressionFunction *> &functions = *sFunctions();
8845
8846 if ( functions.isEmpty() )
8847 {
8849 << QgsExpressionFunction::Parameter( u"expression"_s )
8850 << QgsExpressionFunction::Parameter( u"group_by"_s, true )
8851 << QgsExpressionFunction::Parameter( u"filter"_s, true );
8852
8853 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8854 aggParamsConcat << QgsExpressionFunction::Parameter( u"concatenator"_s, true ) << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
8855
8856 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8857 aggParamsArray << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true );
8858
8859 functions
8860 << new QgsStaticExpressionFunction( u"sqrt"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnSqrt, u"Math"_s )
8861 << new QgsStaticExpressionFunction( u"radians"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"degrees"_s ), fcnRadians, u"Math"_s )
8862 << new QgsStaticExpressionFunction( u"degrees"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"radians"_s ), fcnDegrees, u"Math"_s )
8863 << new QgsStaticExpressionFunction( u"azimuth"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnAzimuth, u"GeometryGroup"_s )
8864 << new QgsStaticExpressionFunction(
8865 u"bearing"_s,
8867 << QgsExpressionFunction::Parameter( u"point_a"_s )
8868 << QgsExpressionFunction::Parameter( u"point_b"_s )
8869 << QgsExpressionFunction::Parameter( u"source_crs"_s, true, QVariant() )
8870 << QgsExpressionFunction::Parameter( u"ellipsoid"_s, true, QVariant() ),
8871 fcnBearing,
8872 u"GeometryGroup"_s
8873 )
8874 << new QgsStaticExpressionFunction( u"inclination"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point_a"_s ) << QgsExpressionFunction::Parameter( u"point_b"_s ), fcnInclination, u"GeometryGroup"_s )
8875 << new QgsStaticExpressionFunction(
8876 u"project"_s,
8878 << QgsExpressionFunction::Parameter( u"point"_s )
8879 << QgsExpressionFunction::Parameter( u"distance"_s )
8880 << QgsExpressionFunction::Parameter( u"azimuth"_s )
8881 << QgsExpressionFunction::Parameter( u"elevation"_s, true, M_PI_2 ),
8882 fcnProject,
8883 u"GeometryGroup"_s
8884 )
8885 << new QgsStaticExpressionFunction( u"abs"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAbs, u"Math"_s )
8886 << new QgsStaticExpressionFunction( u"cos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnCos, u"Math"_s )
8887 << new QgsStaticExpressionFunction( u"sin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnSin, u"Math"_s )
8888 << new QgsStaticExpressionFunction( u"tan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"angle"_s ), fcnTan, u"Math"_s )
8889 << new QgsStaticExpressionFunction( u"asin"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAsin, u"Math"_s )
8890 << new QgsStaticExpressionFunction( u"acos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAcos, u"Math"_s )
8891 << new QgsStaticExpressionFunction( u"atan"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnAtan, u"Math"_s )
8892 << new QgsStaticExpressionFunction( u"atan2"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"dx"_s ) << QgsExpressionFunction::Parameter( u"dy"_s ), fcnAtan2, u"Math"_s )
8893 << new QgsStaticExpressionFunction( u"exp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnExp, u"Math"_s )
8894 << new QgsStaticExpressionFunction( u"ln"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLn, u"Math"_s )
8895 << new QgsStaticExpressionFunction( u"log10"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog10, u"Math"_s )
8896 << new QgsStaticExpressionFunction( u"log"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"base"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnLog, u"Math"_s )
8897 << new QgsStaticExpressionFunction( u"round"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ) << QgsExpressionFunction::Parameter( u"places"_s, true, 0 ), fcnRound, u"Math"_s );
8898
8899 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction(
8900 u"rand"_s,
8901 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"min"_s ) << QgsExpressionFunction::Parameter( u"max"_s ) << QgsExpressionFunction::Parameter( u"seed"_s, true ),
8902 fcnRnd,
8903 u"Math"_s
8904 );
8905 randFunc->setIsStatic( false );
8906 functions << randFunc;
8907
8908 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction(
8909 u"randf"_s,
8911 << QgsExpressionFunction::Parameter( u"min"_s, true, 0.0 )
8912 << QgsExpressionFunction::Parameter( u"max"_s, true, 1.0 )
8913 << QgsExpressionFunction::Parameter( u"seed"_s, true ),
8914 fcnRndF,
8915 u"Math"_s
8916 );
8917 randfFunc->setIsStatic( false );
8918 functions << randfFunc;
8919
8920 functions
8921 << new QgsStaticExpressionFunction( u"max"_s, -1, fcnMax, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8922 << new QgsStaticExpressionFunction( u"min"_s, -1, fcnMin, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8923 << 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 )
8924 << new QgsStaticExpressionFunction(
8925 u"scale_linear"_s,
8927 << QgsExpressionFunction::Parameter( u"value"_s )
8928 << QgsExpressionFunction::Parameter( u"domain_min"_s )
8929 << QgsExpressionFunction::Parameter( u"domain_max"_s )
8930 << QgsExpressionFunction::Parameter( u"range_min"_s )
8931 << QgsExpressionFunction::Parameter( u"range_max"_s ),
8932 fcnLinearScale,
8933 u"Math"_s
8934 )
8935 << new QgsStaticExpressionFunction(
8936 u"scale_polynomial"_s,
8938 << QgsExpressionFunction::Parameter( u"value"_s )
8939 << QgsExpressionFunction::Parameter( u"domain_min"_s )
8940 << QgsExpressionFunction::Parameter( u"domain_max"_s )
8941 << QgsExpressionFunction::Parameter( u"range_min"_s )
8942 << QgsExpressionFunction::Parameter( u"range_max"_s )
8943 << QgsExpressionFunction::Parameter( u"exponent"_s ),
8944 fcnPolynomialScale,
8945 u"Math"_s,
8946 QString(),
8947 false,
8948 QSet<QString>(),
8949 false,
8950 QStringList() << u"scale_exp"_s
8951 )
8952 << new QgsStaticExpressionFunction(
8953 u"scale_exponential"_s,
8955 << QgsExpressionFunction::Parameter( u"value"_s )
8956 << QgsExpressionFunction::Parameter( u"domain_min"_s )
8957 << QgsExpressionFunction::Parameter( u"domain_max"_s )
8958 << QgsExpressionFunction::Parameter( u"range_min"_s )
8959 << QgsExpressionFunction::Parameter( u"range_max"_s )
8960 << QgsExpressionFunction::Parameter( u"exponent"_s ),
8961 fcnExponentialScale,
8962 u"Math"_s
8963 )
8964 << new QgsStaticExpressionFunction( u"floor"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnFloor, u"Math"_s )
8965 << new QgsStaticExpressionFunction( u"ceil"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnCeil, u"Math"_s )
8966 << new QgsStaticExpressionFunction( u"pi"_s, 0, fcnPi, u"Math"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$pi"_s )
8967 << 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 )
8968 << 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 )
8969 << 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 )
8970 << new QgsStaticExpressionFunction(
8971 u"to_string"_s,
8972 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
8973 fcnToString,
8974 QStringList() << u"Conversions"_s << u"String"_s,
8975 QString(),
8976 false,
8977 QSet<QString>(),
8978 false,
8979 QStringList() << u"tostring"_s
8980 )
8981 << new QgsStaticExpressionFunction(
8982 u"to_datetime"_s,
8984 << QgsExpressionFunction::Parameter( u"value"_s )
8985 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
8986 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
8987 fcnToDateTime,
8988 QStringList() << u"Conversions"_s << u"Date and Time"_s,
8989 QString(),
8990 false,
8991 QSet<QString>(),
8992 false,
8993 QStringList() << u"todatetime"_s
8994 )
8995 << new QgsStaticExpressionFunction(
8996 u"to_date"_s,
8998 << QgsExpressionFunction::Parameter( u"value"_s )
8999 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9000 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9001 fcnToDate,
9002 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9003 QString(),
9004 false,
9005 QSet<QString>(),
9006 false,
9007 QStringList() << u"todate"_s
9008 )
9009 << new QgsStaticExpressionFunction(
9010 u"to_time"_s,
9012 << QgsExpressionFunction::Parameter( u"value"_s )
9013 << QgsExpressionFunction::Parameter( u"format"_s, true, QVariant() )
9014 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9015 fcnToTime,
9016 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9017 QString(),
9018 false,
9019 QSet<QString>(),
9020 false,
9021 QStringList() << u"totime"_s
9022 )
9023 << new QgsStaticExpressionFunction(
9024 u"to_interval"_s,
9025 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ),
9026 fcnToInterval,
9027 QStringList() << u"Conversions"_s << u"Date and Time"_s,
9028 QString(),
9029 false,
9030 QSet<QString>(),
9031 false,
9032 QStringList() << u"tointerval"_s
9033 )
9034 << new QgsStaticExpressionFunction(
9035 u"to_dm"_s,
9037 << QgsExpressionFunction::Parameter( u"value"_s )
9038 << QgsExpressionFunction::Parameter( u"axis"_s )
9039 << QgsExpressionFunction::Parameter( u"precision"_s )
9040 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9041 fcnToDegreeMinute,
9042 u"Conversions"_s,
9043 QString(),
9044 false,
9045 QSet<QString>(),
9046 false,
9047 QStringList() << u"todm"_s
9048 )
9049 << new QgsStaticExpressionFunction(
9050 u"to_dms"_s,
9052 << QgsExpressionFunction::Parameter( u"value"_s )
9053 << QgsExpressionFunction::Parameter( u"axis"_s )
9054 << QgsExpressionFunction::Parameter( u"precision"_s )
9055 << QgsExpressionFunction::Parameter( u"formatting"_s, true ),
9056 fcnToDegreeMinuteSecond,
9057 u"Conversions"_s,
9058 QString(),
9059 false,
9060 QSet<QString>(),
9061 false,
9062 QStringList() << u"todms"_s
9063 )
9064 << 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 )
9065 << new QgsStaticExpressionFunction( u"extract_degrees"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractDegrees, u"Conversions"_s )
9066 << new QgsStaticExpressionFunction( u"extract_minutes"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractMinutes, u"Conversions"_s )
9067 << new QgsStaticExpressionFunction( u"extract_seconds"_s, { QgsExpressionFunction::Parameter { u"value"_s } }, fcnExtractSeconds, u"Conversions"_s )
9068 << new QgsStaticExpressionFunction( u"coalesce"_s, -1, fcnCoalesce, u"Conditionals"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9069 << new QgsStaticExpressionFunction( u"nullif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value1"_s ) << QgsExpressionFunction::Parameter( u"value2"_s ), fcnNullIf, u"Conditionals"_s )
9070 << new QgsStaticExpressionFunction(
9071 u"if"_s,
9073 << QgsExpressionFunction::Parameter( u"condition"_s )
9074 << QgsExpressionFunction::Parameter( u"result_when_true"_s )
9075 << QgsExpressionFunction::Parameter( u"result_when_false"_s ),
9076 fcnIf,
9077 u"Conditionals"_s,
9078 QString(),
9079 false,
9080 QSet<QString>(),
9081 true
9082 )
9083 << new QgsStaticExpressionFunction(
9084 u"try"_s,
9085 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ) << QgsExpressionFunction::Parameter( u"alternative"_s, true, QVariant() ),
9086 fcnTry,
9087 u"Conditionals"_s,
9088 QString(),
9089 false,
9090 QSet<QString>(),
9091 true
9092 )
9093
9094 << new QgsStaticExpressionFunction(
9095 u"aggregate"_s,
9097 << QgsExpressionFunction::Parameter( u"layer"_s )
9098 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9099 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9100 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9101 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9102 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9103 fcnAggregate,
9104 u"Aggregates"_s,
9105 QString(),
9106 []( const QgsExpressionNodeFunction *node ) {
9107 // usesGeometry callback: return true if @parent variable is referenced
9108
9109 if ( !node )
9110 return true;
9111
9112 if ( !node->args() )
9113 return false;
9114
9115 QSet<QString> referencedVars;
9116 if ( node->args()->count() > 2 )
9117 {
9118 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9119 referencedVars = subExpressionNode->referencedVariables();
9120 }
9121
9122 if ( node->args()->count() > 3 )
9123 {
9124 QgsExpressionNode *filterNode = node->args()->at( 3 );
9125 referencedVars.unite( filterNode->referencedVariables() );
9126 }
9127 return referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() );
9128 },
9129 []( const QgsExpressionNodeFunction *node ) {
9130 // referencedColumns callback: return AllAttributes if @parent variable is referenced
9131
9132 if ( !node )
9133 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9134
9135 if ( !node->args() )
9136 return QSet<QString>();
9137
9138 QSet<QString> referencedCols;
9139 QSet<QString> referencedVars;
9140
9141 if ( node->args()->count() > 2 )
9142 {
9143 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
9144 referencedVars = subExpressionNode->referencedVariables();
9145 referencedCols = subExpressionNode->referencedColumns();
9146 }
9147 if ( node->args()->count() > 3 )
9148 {
9149 QgsExpressionNode *filterNode = node->args()->at( 3 );
9150 referencedVars = filterNode->referencedVariables();
9151 referencedCols.unite( filterNode->referencedColumns() );
9152 }
9153
9154 if ( referencedVars.contains( u"parent"_s ) || referencedVars.contains( QString() ) )
9155 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
9156 else
9157 return referencedCols;
9158 },
9159 true
9160 )
9161
9162 << new QgsStaticExpressionFunction(
9163 u"relation_aggregate"_s,
9165 << QgsExpressionFunction::Parameter( u"relation"_s )
9166 << QgsExpressionFunction::Parameter( u"aggregate"_s )
9167 << QgsExpressionFunction::Parameter( u"expression"_s, false, QVariant(), true )
9168 << QgsExpressionFunction::Parameter( u"concatenator"_s, true )
9169 << QgsExpressionFunction::Parameter( u"order_by"_s, true, QVariant(), true ),
9170 fcnAggregateRelation,
9171 u"Aggregates"_s,
9172 QString(),
9173 false,
9174 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9175 true
9176 )
9177
9178 << new QgsStaticExpressionFunction( u"count"_s, aggParams, fcnAggregateCount, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9179 << new QgsStaticExpressionFunction( u"count_distinct"_s, aggParams, fcnAggregateCountDistinct, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9180 << new QgsStaticExpressionFunction( u"count_missing"_s, aggParams, fcnAggregateCountMissing, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9181 << new QgsStaticExpressionFunction( u"minimum"_s, aggParams, fcnAggregateMin, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9182 << new QgsStaticExpressionFunction( u"maximum"_s, aggParams, fcnAggregateMax, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9183 << new QgsStaticExpressionFunction( u"sum"_s, aggParams, fcnAggregateSum, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9184 << new QgsStaticExpressionFunction( u"mean"_s, aggParams, fcnAggregateMean, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9185 << new QgsStaticExpressionFunction( u"median"_s, aggParams, fcnAggregateMedian, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9186 << new QgsStaticExpressionFunction( u"stdev"_s, aggParams, fcnAggregateStdev, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9187 << new QgsStaticExpressionFunction( u"range"_s, aggParams, fcnAggregateRange, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9188 << new QgsStaticExpressionFunction( u"minority"_s, aggParams, fcnAggregateMinority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9189 << new QgsStaticExpressionFunction( u"majority"_s, aggParams, fcnAggregateMajority, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9190 << new QgsStaticExpressionFunction( u"q1"_s, aggParams, fcnAggregateQ1, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9191 << new QgsStaticExpressionFunction( u"q3"_s, aggParams, fcnAggregateQ3, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9192 << new QgsStaticExpressionFunction( u"iqr"_s, aggParams, fcnAggregateIQR, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9193 << new QgsStaticExpressionFunction( u"min_length"_s, aggParams, fcnAggregateMinLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9194 << new QgsStaticExpressionFunction( u"max_length"_s, aggParams, fcnAggregateMaxLength, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9195 << new QgsStaticExpressionFunction( u"collect"_s, aggParams, fcnAggregateCollectGeometry, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9196 << new QgsStaticExpressionFunction( u"concatenate"_s, aggParamsConcat, fcnAggregateStringConcat, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9197 << new QgsStaticExpressionFunction( u"concatenate_unique"_s, aggParamsConcat, fcnAggregateStringConcatUnique, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9198 << new QgsStaticExpressionFunction( u"array_agg"_s, aggParamsArray, fcnAggregateArray, u"Aggregates"_s, QString(), false, QSet<QString>(), true )
9199
9200 << 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 )
9201 << new QgsStaticExpressionFunction(
9202 u"regexp_matches"_s,
9204 << QgsExpressionFunction::Parameter( u"string"_s )
9205 << QgsExpressionFunction::Parameter( u"regex"_s )
9206 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
9207 fcnRegexpMatches,
9208 u"Arrays"_s
9209 )
9210
9211 << new QgsStaticExpressionFunction( u"now"_s, 0, fcnNow, u"Date and Time"_s, QString(), false, QSet<QString>(), false, QStringList() << u"$now"_s )
9212 << new QgsStaticExpressionFunction( u"age"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime1"_s ) << QgsExpressionFunction::Parameter( u"datetime2"_s ), fcnAge, u"Date and Time"_s )
9213 << new QgsStaticExpressionFunction( u"year"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnYear, u"Date and Time"_s )
9214 << new QgsStaticExpressionFunction( u"month"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnMonth, u"Date and Time"_s )
9215 << new QgsStaticExpressionFunction( u"week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnWeek, u"Date and Time"_s )
9216 << new QgsStaticExpressionFunction( u"day"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDay, u"Date and Time"_s )
9217 << new QgsStaticExpressionFunction( u"hour"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnHour, u"Date and Time"_s )
9218 << new QgsStaticExpressionFunction( u"minute"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnMinute, u"Date and Time"_s )
9219 << new QgsStaticExpressionFunction( u"second"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"datetime"_s ), fcnSeconds, u"Date and Time"_s )
9220 << new QgsStaticExpressionFunction( u"epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnEpoch, u"Date and Time"_s )
9221 << new QgsStaticExpressionFunction( u"datetime_from_epoch"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"long"_s ), fcnDateTimeFromEpoch, u"Date and Time"_s )
9222 << new QgsStaticExpressionFunction( u"day_of_week"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"date"_s ), fcnDayOfWeek, u"Date and Time"_s )
9223 << 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 )
9224 << new QgsStaticExpressionFunction(
9225 u"make_time"_s,
9226 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hour"_s ) << QgsExpressionFunction::Parameter( u"minute"_s ) << QgsExpressionFunction::Parameter( u"second"_s ),
9227 fcnMakeTime,
9228 u"Date and Time"_s
9229 )
9230 << new QgsStaticExpressionFunction(
9231 u"make_datetime"_s,
9233 << QgsExpressionFunction::Parameter( u"year"_s )
9234 << QgsExpressionFunction::Parameter( u"month"_s )
9235 << QgsExpressionFunction::Parameter( u"day"_s )
9236 << QgsExpressionFunction::Parameter( u"hour"_s )
9237 << QgsExpressionFunction::Parameter( u"minute"_s )
9238 << QgsExpressionFunction::Parameter( u"second"_s ),
9239 fcnMakeDateTime,
9240 u"Date and Time"_s
9241 )
9242 << new QgsStaticExpressionFunction(
9243 u"make_interval"_s,
9245 << QgsExpressionFunction::Parameter( u"years"_s, true, 0 )
9246 << QgsExpressionFunction::Parameter( u"months"_s, true, 0 )
9247 << QgsExpressionFunction::Parameter( u"weeks"_s, true, 0 )
9248 << QgsExpressionFunction::Parameter( u"days"_s, true, 0 )
9249 << QgsExpressionFunction::Parameter( u"hours"_s, true, 0 )
9250 << QgsExpressionFunction::Parameter( u"minutes"_s, true, 0 )
9251 << QgsExpressionFunction::Parameter( u"seconds"_s, true, 0 ),
9252 fcnMakeInterval,
9253 u"Date and Time"_s
9254 )
9255 << new QgsStaticExpressionFunction( u"timezone_from_id"_s, { QgsExpressionFunction::Parameter( u"id"_s ) }, fcnTimeZoneFromId, u"Date and Time"_s )
9256 << new QgsStaticExpressionFunction( u"timezone_id"_s, { QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnTimeZoneToId, u"Date and Time"_s )
9257 << new QgsStaticExpressionFunction( u"get_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ) }, fcnGetTimeZone, u"Date and Time"_s )
9258 << new QgsStaticExpressionFunction( u"set_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnSetTimeZone, u"Date and Time"_s )
9259 << new QgsStaticExpressionFunction( u"convert_timezone"_s, { QgsExpressionFunction::Parameter( u"datetime"_s ), QgsExpressionFunction::Parameter( u"timezone"_s ) }, fcnConvertTimeZone, u"Date and Time"_s )
9260 << new QgsStaticExpressionFunction( u"lower"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnLower, u"String"_s )
9261 << new QgsStaticExpressionFunction(
9262 u"substr_count"_s,
9264 << QgsExpressionFunction::Parameter( u"string"_s )
9265 << QgsExpressionFunction::Parameter( u"substring"_s )
9266 << QgsExpressionFunction::Parameter( u"overlapping"_s, true, false ), // Optional parameter with default value of false
9267 fcnSubstrCount,
9268 u"String"_s
9269 )
9270 << new QgsStaticExpressionFunction( u"upper"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnUpper, u"String"_s )
9271 << new QgsStaticExpressionFunction( u"title"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTitle, u"String"_s )
9272 << new QgsStaticExpressionFunction( u"trim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnTrim, u"String"_s )
9273 << new QgsStaticExpressionFunction( u"unaccent"_s, { QgsExpressionFunction::Parameter( u"string"_s ) }, fcnUnaccent, u"String"_s )
9274 << new QgsStaticExpressionFunction( u"ltrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnLTrim, u"String"_s )
9275 << new QgsStaticExpressionFunction( u"rtrim"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"characters"_s, true, u" "_s ), fcnRTrim, u"String"_s )
9276 << new QgsStaticExpressionFunction( u"levenshtein"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLevenshtein, u"Fuzzy Matching"_s )
9277 << new QgsStaticExpressionFunction( u"longest_common_substring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnLCS, u"Fuzzy Matching"_s )
9278 << new QgsStaticExpressionFunction( u"hamming_distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string1"_s ) << QgsExpressionFunction::Parameter( u"string2"_s ), fcnHamming, u"Fuzzy Matching"_s )
9279 << new QgsStaticExpressionFunction( u"soundex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnSoundex, u"Fuzzy Matching"_s )
9280 << new QgsStaticExpressionFunction( u"char"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"code"_s ), fcnChar, u"String"_s )
9281 << new QgsStaticExpressionFunction( u"ascii"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnAscii, u"String"_s )
9282 << new QgsStaticExpressionFunction(
9283 u"wordwrap"_s,
9285 << QgsExpressionFunction::Parameter( u"text"_s )
9286 << QgsExpressionFunction::Parameter( u"length"_s )
9287 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "" ),
9288 fcnWordwrap,
9289 u"String"_s
9290 )
9291 << new QgsStaticExpressionFunction( u"length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"text"_s, true, "" ), fcnLength, QStringList() << u"String"_s << u"GeometryGroup"_s )
9292 << new QgsStaticExpressionFunction( u"length3D"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLength3D, u"GeometryGroup"_s )
9293 << new QgsStaticExpressionFunction( u"repeat"_s, { QgsExpressionFunction::Parameter( u"text"_s ), QgsExpressionFunction::Parameter( u"number"_s ) }, fcnRepeat, u"String"_s )
9294 << new QgsStaticExpressionFunction( u"replace"_s, -1, fcnReplace, u"String"_s )
9295 << new QgsStaticExpressionFunction(
9296 u"regexp_replace"_s,
9297 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ) << QgsExpressionFunction::Parameter( u"replacement"_s ),
9298 fcnRegexpReplace,
9299 u"String"_s
9300 )
9301 << new QgsStaticExpressionFunction( u"regexp_substr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"input_string"_s ) << QgsExpressionFunction::Parameter( u"regex"_s ), fcnRegexpSubstr, u"String"_s )
9302 << new QgsStaticExpressionFunction(
9303 u"substr"_s,
9304 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"length"_s, true ),
9305 fcnSubstr,
9306 u"String"_s,
9307 QString(),
9308 false,
9309 QSet< QString >(),
9310 false,
9311 QStringList(),
9312 true
9313 )
9314 << new QgsStaticExpressionFunction( u"concat"_s, -1, fcnConcat, u"String"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
9315 << new QgsStaticExpressionFunction( u"strpos"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"haystack"_s ) << QgsExpressionFunction::Parameter( u"needle"_s ), fcnStrpos, u"String"_s )
9316 << new QgsStaticExpressionFunction( u"left"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnLeft, u"String"_s )
9317 << new QgsStaticExpressionFunction( u"right"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"length"_s ), fcnRight, u"String"_s )
9318 << new QgsStaticExpressionFunction( u"rpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnRPad, u"String"_s )
9319 << new QgsStaticExpressionFunction( u"lpad"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"width"_s ) << QgsExpressionFunction::Parameter( u"fill"_s ), fcnLPad, u"String"_s )
9320 << new QgsStaticExpressionFunction( u"format"_s, -1, fcnFormatString, u"String"_s )
9321 << new QgsStaticExpressionFunction(
9322 u"format_number"_s,
9324 << QgsExpressionFunction::Parameter( u"number"_s )
9325 << QgsExpressionFunction::Parameter( u"places"_s, true, 0 )
9326 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() )
9327 << QgsExpressionFunction::Parameter( u"omit_group_separators"_s, true, false )
9328 << QgsExpressionFunction::Parameter( u"trim_trailing_zeroes"_s, true, false ),
9329 fcnFormatNumber,
9330 u"String"_s
9331 )
9332 << new QgsStaticExpressionFunction(
9333 u"format_date"_s,
9335 << QgsExpressionFunction::Parameter( u"datetime"_s )
9336 << QgsExpressionFunction::Parameter( u"format"_s )
9337 << QgsExpressionFunction::Parameter( u"language"_s, true, QVariant() ),
9338 fcnFormatDate,
9339 QStringList() << u"String"_s << u"Date and Time"_s
9340 )
9341 << new QgsStaticExpressionFunction( u"color_grayscale_average"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ), fcnColorGrayscaleAverage, u"Color"_s )
9342 << new QgsStaticExpressionFunction(
9343 u"color_mix_rgb"_s,
9344 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9345 fcnColorMixRgb,
9346 u"Color"_s
9347 )
9348 << new QgsStaticExpressionFunction(
9349 u"color_mix"_s,
9350 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color1"_s ) << QgsExpressionFunction::Parameter( u"color2"_s ) << QgsExpressionFunction::Parameter( u"ratio"_s ),
9351 fcnColorMix,
9352 u"Color"_s
9353 )
9354 << 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 )
9355 << new QgsStaticExpressionFunction(
9356 u"color_rgbf"_s,
9358 << QgsExpressionFunction::Parameter( u"red"_s )
9359 << QgsExpressionFunction::Parameter( u"green"_s )
9360 << QgsExpressionFunction::Parameter( u"blue"_s )
9361 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9362 fcnColorRgbF,
9363 u"Color"_s
9364 )
9365 << new QgsStaticExpressionFunction(
9366 u"color_rgba"_s,
9368 << QgsExpressionFunction::Parameter( u"red"_s )
9369 << QgsExpressionFunction::Parameter( u"green"_s )
9370 << QgsExpressionFunction::Parameter( u"blue"_s )
9371 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9372 fncColorRgba,
9373 u"Color"_s
9374 )
9375 << new QgsStaticExpressionFunction( u"ramp_color"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColor, u"Color"_s )
9376 << new QgsStaticExpressionFunction( u"ramp_color_object"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"ramp_name"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnRampColorObject, u"Color"_s )
9377 << new QgsStaticExpressionFunction( u"create_ramp"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"discrete"_s, true, false ), fcnCreateRamp, u"Color"_s )
9378 << new QgsStaticExpressionFunction(
9379 u"color_hsl"_s,
9380 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"lightness"_s ),
9381 fcnColorHsl,
9382 u"Color"_s
9383 )
9384 << new QgsStaticExpressionFunction(
9385 u"color_hsla"_s,
9387 << QgsExpressionFunction::Parameter( u"hue"_s )
9388 << QgsExpressionFunction::Parameter( u"saturation"_s )
9389 << QgsExpressionFunction::Parameter( u"lightness"_s )
9390 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9391 fncColorHsla,
9392 u"Color"_s
9393 )
9394 << new QgsStaticExpressionFunction(
9395 u"color_hslf"_s,
9397 << QgsExpressionFunction::Parameter( u"hue"_s )
9398 << QgsExpressionFunction::Parameter( u"saturation"_s )
9399 << QgsExpressionFunction::Parameter( u"lightness"_s )
9400 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9401 fcnColorHslF,
9402 u"Color"_s
9403 )
9404 << new QgsStaticExpressionFunction(
9405 u"color_hsv"_s,
9406 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"hue"_s ) << QgsExpressionFunction::Parameter( u"saturation"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9407 fcnColorHsv,
9408 u"Color"_s
9409 )
9410 << new QgsStaticExpressionFunction(
9411 u"color_hsva"_s,
9413 << QgsExpressionFunction::Parameter( u"hue"_s )
9414 << QgsExpressionFunction::Parameter( u"saturation"_s )
9415 << QgsExpressionFunction::Parameter( u"value"_s )
9416 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9417 fncColorHsva,
9418 u"Color"_s
9419 )
9420 << new QgsStaticExpressionFunction(
9421 u"color_hsvf"_s,
9423 << QgsExpressionFunction::Parameter( u"hue"_s )
9424 << QgsExpressionFunction::Parameter( u"saturation"_s )
9425 << QgsExpressionFunction::Parameter( u"value"_s )
9426 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9427 fcnColorHsvF,
9428 u"Color"_s
9429 )
9430 << new QgsStaticExpressionFunction(
9431 u"color_cmyk"_s,
9433 << QgsExpressionFunction::Parameter( u"cyan"_s )
9434 << QgsExpressionFunction::Parameter( u"magenta"_s )
9435 << QgsExpressionFunction::Parameter( u"yellow"_s )
9436 << QgsExpressionFunction::Parameter( u"black"_s ),
9437 fcnColorCmyk,
9438 u"Color"_s
9439 )
9440 << new QgsStaticExpressionFunction(
9441 u"color_cmyka"_s,
9443 << QgsExpressionFunction::Parameter( u"cyan"_s )
9444 << QgsExpressionFunction::Parameter( u"magenta"_s )
9445 << QgsExpressionFunction::Parameter( u"yellow"_s )
9446 << QgsExpressionFunction::Parameter( u"black"_s )
9447 << QgsExpressionFunction::Parameter( u"alpha"_s ),
9448 fncColorCmyka,
9449 u"Color"_s
9450 )
9451 << new QgsStaticExpressionFunction(
9452 u"color_cmykf"_s,
9454 << QgsExpressionFunction::Parameter( u"cyan"_s )
9455 << QgsExpressionFunction::Parameter( u"magenta"_s )
9456 << QgsExpressionFunction::Parameter( u"yellow"_s )
9457 << QgsExpressionFunction::Parameter( u"black"_s )
9458 << QgsExpressionFunction::Parameter( u"alpha"_s, true, 1. ),
9459 fcnColorCmykF,
9460 u"Color"_s
9461 )
9462 << new QgsStaticExpressionFunction( u"color_part"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ), fncColorPart, u"Color"_s )
9463 << new QgsStaticExpressionFunction( u"darker"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncDarker, u"Color"_s )
9464 << new QgsStaticExpressionFunction( u"lighter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"factor"_s ), fncLighter, u"Color"_s )
9465 << new QgsStaticExpressionFunction(
9466 u"set_color_part"_s,
9467 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"color"_s ) << QgsExpressionFunction::Parameter( u"component"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
9468 fncSetColorPart,
9469 u"Color"_s
9470 )
9471
9472 // file info
9473 << new QgsStaticExpressionFunction( u"base_file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnBaseFileName, u"Files and Paths"_s )
9474 << new QgsStaticExpressionFunction( u"file_suffix"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSuffix, u"Files and Paths"_s )
9475 << new QgsStaticExpressionFunction( u"file_exists"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileExists, u"Files and Paths"_s )
9476 << new QgsStaticExpressionFunction( u"file_name"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileName, u"Files and Paths"_s )
9477 << new QgsStaticExpressionFunction( u"is_file"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsFile, u"Files and Paths"_s )
9478 << new QgsStaticExpressionFunction( u"is_directory"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnPathIsDir, u"Files and Paths"_s )
9479 << new QgsStaticExpressionFunction( u"file_path"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFilePath, u"Files and Paths"_s )
9480 << new QgsStaticExpressionFunction( u"file_size"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnFileSize, u"Files and Paths"_s )
9481
9482 << new QgsStaticExpressionFunction( u"exif"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ) << QgsExpressionFunction::Parameter( u"tag"_s, true ), fcnExif, u"Files and Paths"_s )
9483 << new QgsStaticExpressionFunction( u"exif_geotag"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"path"_s ), fcnExifGeoTag, u"GeometryGroup"_s )
9484
9485 // hash
9486 << new QgsStaticExpressionFunction( u"hash"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ) << QgsExpressionFunction::Parameter( u"method"_s ), fcnGenericHash, u"Conversions"_s )
9487 << new QgsStaticExpressionFunction( u"md5"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashMd5, u"Conversions"_s )
9488 << new QgsStaticExpressionFunction( u"sha256"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHashSha256, u"Conversions"_s )
9489
9490 //base64
9491 << new QgsStaticExpressionFunction( u"to_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"value"_s ), fcnToBase64, u"Conversions"_s )
9492 << new QgsStaticExpressionFunction( u"from_base64"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnFromBase64, u"Conversions"_s )
9493
9494 // magnetic models
9495 << new QgsStaticExpressionFunction(
9496 u"magnetic_declination"_s,
9498 << QgsExpressionFunction::Parameter( u"model_name"_s )
9499 << QgsExpressionFunction::Parameter( u"date"_s )
9500 << QgsExpressionFunction::Parameter( u"latitude"_s )
9501 << QgsExpressionFunction::Parameter( u"longitude"_s )
9502 << QgsExpressionFunction::Parameter( u"height"_s )
9503 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9504 fcnMagneticDeclination,
9505 u"MagneticModels"_s
9506 )
9507 << new QgsStaticExpressionFunction(
9508 u"magnetic_inclination"_s,
9510 << QgsExpressionFunction::Parameter( u"model_name"_s )
9511 << QgsExpressionFunction::Parameter( u"date"_s )
9512 << QgsExpressionFunction::Parameter( u"latitude"_s )
9513 << QgsExpressionFunction::Parameter( u"longitude"_s )
9514 << QgsExpressionFunction::Parameter( u"height"_s )
9515 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9516 fcnMagneticInclination,
9517 u"MagneticModels"_s
9518 )
9519 << new QgsStaticExpressionFunction(
9520 u"magnetic_declination_rate_of_change"_s,
9522 << QgsExpressionFunction::Parameter( u"model_name"_s )
9523 << QgsExpressionFunction::Parameter( u"date"_s )
9524 << QgsExpressionFunction::Parameter( u"latitude"_s )
9525 << QgsExpressionFunction::Parameter( u"longitude"_s )
9526 << QgsExpressionFunction::Parameter( u"height"_s )
9527 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9528 fcnMagneticDeclinationRateOfChange,
9529 u"MagneticModels"_s
9530 )
9531 << new QgsStaticExpressionFunction(
9532 u"magnetic_inclination_rate_of_change"_s,
9534 << QgsExpressionFunction::Parameter( u"model_name"_s )
9535 << QgsExpressionFunction::Parameter( u"date"_s )
9536 << QgsExpressionFunction::Parameter( u"latitude"_s )
9537 << QgsExpressionFunction::Parameter( u"longitude"_s )
9538 << QgsExpressionFunction::Parameter( u"height"_s )
9539 << QgsExpressionFunction::Parameter( u"model_path"_s, true ),
9540 fcnMagneticInclinationRateOfChange,
9541 u"MagneticModels"_s
9542 )
9543
9544 // deprecated stuff - hidden from users
9545 << new QgsStaticExpressionFunction( u"$scale"_s, QgsExpressionFunction::ParameterList(), fcnMapScale, u"deprecated"_s );
9546
9547 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( u"$geometry"_s, 0, fcnGeometry, u"GeometryGroup"_s, QString(), true );
9548 geomFunc->setIsStatic( false );
9549 functions << geomFunc;
9550
9551 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( u"$area"_s, 0, fcnGeomArea, u"GeometryGroup"_s, QString(), true );
9552 areaFunc->setIsStatic( false );
9553 functions << areaFunc;
9554
9555 functions << new QgsStaticExpressionFunction( u"area"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnArea, u"GeometryGroup"_s );
9556
9557 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( u"$length"_s, 0, fcnGeomLength, u"GeometryGroup"_s, QString(), true );
9558 lengthFunc->setIsStatic( false );
9559 functions << lengthFunc;
9560
9561 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( u"$perimeter"_s, 0, fcnGeomPerimeter, u"GeometryGroup"_s, QString(), true );
9562 perimeterFunc->setIsStatic( false );
9563 functions << perimeterFunc;
9564
9565 functions << new QgsStaticExpressionFunction( u"perimeter"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPerimeter, u"GeometryGroup"_s );
9566
9567 functions << new QgsStaticExpressionFunction( u"roundness"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnRoundness, u"GeometryGroup"_s );
9568
9569 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( u"$x"_s, 0, fcnX, u"GeometryGroup"_s, QString(), true );
9570 xFunc->setIsStatic( false );
9571 functions << xFunc;
9572
9573 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( u"$y"_s, 0, fcnY, u"GeometryGroup"_s, QString(), true );
9574 yFunc->setIsStatic( false );
9575 functions << yFunc;
9576
9577 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( u"$z"_s, 0, fcnZ, u"GeometryGroup"_s, QString(), true );
9578 zFunc->setIsStatic( false );
9579 functions << zFunc;
9580
9581 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions {
9582 { u"overlay_intersects"_s, fcnGeomOverlayIntersects },
9583 { u"overlay_contains"_s, fcnGeomOverlayContains },
9584 { u"overlay_crosses"_s, fcnGeomOverlayCrosses },
9585 { u"overlay_equals"_s, fcnGeomOverlayEquals },
9586 { u"overlay_touches"_s, fcnGeomOverlayTouches },
9587 { u"overlay_disjoint"_s, fcnGeomOverlayDisjoint },
9588 { u"overlay_within"_s, fcnGeomOverlayWithin },
9589 };
9590 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9591 while ( i.hasNext() )
9592 {
9593 i.next();
9594 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction(
9595 i.key(),
9597 << QgsExpressionFunction::Parameter( u"layer"_s )
9598 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9599 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9600 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( -1 ), true )
9601 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false )
9602 << QgsExpressionFunction::Parameter( u"min_overlap"_s, true, QVariant( -1 ), false )
9603 << QgsExpressionFunction::Parameter( u"min_inscribed_circle_radius"_s, true, QVariant( -1 ), false )
9604 << QgsExpressionFunction::Parameter( u"return_details"_s, true, false, false )
9605 << QgsExpressionFunction::Parameter( u"sort_by_intersection_size"_s, true, QString(), false ),
9606 i.value(),
9607 u"GeometryGroup"_s,
9608 QString(),
9609 true,
9610 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9611 true
9612 );
9613
9614 // The current feature is accessed for the geometry, so this should not be cached
9615 fcnGeomOverlayFunc->setIsStatic( false );
9616 functions << fcnGeomOverlayFunc;
9617 }
9618
9619 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction(
9620 u"overlay_nearest"_s,
9622 << QgsExpressionFunction::Parameter( u"layer"_s )
9623 << QgsExpressionFunction::Parameter( u"expression"_s, true, QVariant(), true )
9624 << QgsExpressionFunction::Parameter( u"filter"_s, true, QVariant(), true )
9625 << QgsExpressionFunction::Parameter( u"limit"_s, true, QVariant( 1 ), true )
9626 << QgsExpressionFunction::Parameter( u"max_distance"_s, true, 0 )
9627 << QgsExpressionFunction::Parameter( u"cache"_s, true, QVariant( false ), false ),
9628 fcnGeomOverlayNearest,
9629 u"GeometryGroup"_s,
9630 QString(),
9631 true,
9632 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES,
9633 true
9634 );
9635 // The current feature is accessed for the geometry, so this should not be cached
9636 fcnGeomOverlayNearestFunc->setIsStatic( false );
9637 functions << fcnGeomOverlayNearestFunc;
9638
9639 functions
9640 << new QgsStaticExpressionFunction( u"is_valid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsValid, u"GeometryGroup"_s )
9641 << new QgsStaticExpressionFunction( u"x"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomX, u"GeometryGroup"_s )
9642 << new QgsStaticExpressionFunction( u"y"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomY, u"GeometryGroup"_s )
9643 << new QgsStaticExpressionFunction( u"z"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomZ, u"GeometryGroup"_s )
9644 << new QgsStaticExpressionFunction( u"m"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomM, u"GeometryGroup"_s )
9645 << new QgsStaticExpressionFunction( u"point_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnPointN, u"GeometryGroup"_s )
9646 << new QgsStaticExpressionFunction( u"start_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStartPoint, u"GeometryGroup"_s )
9647 << new QgsStaticExpressionFunction( u"end_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnEndPoint, u"GeometryGroup"_s )
9648 << 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 )
9649 << new QgsStaticExpressionFunction( u"segments_to_lines"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSegmentsToLines, u"GeometryGroup"_s )
9650 << new QgsStaticExpressionFunction( u"collect_geometries"_s, -1, fcnCollectGeometries, u"GeometryGroup"_s )
9651 << new QgsStaticExpressionFunction( u"make_point"_s, -1, fcnMakePoint, u"GeometryGroup"_s )
9652 << 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 )
9653 << new QgsStaticExpressionFunction( u"make_line"_s, -1, fcnMakeLine, u"GeometryGroup"_s )
9654 << new QgsStaticExpressionFunction( u"make_polygon"_s, -1, fcnMakePolygon, u"GeometryGroup"_s )
9655 << new QgsStaticExpressionFunction(
9656 u"make_triangle"_s,
9657 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ) << QgsExpressionFunction::Parameter( u"point3"_s ),
9658 fcnMakeTriangle,
9659 u"GeometryGroup"_s
9660 )
9661 << new QgsStaticExpressionFunction(
9662 u"make_circle"_s,
9664 << QgsExpressionFunction::Parameter( u"center"_s )
9665 << QgsExpressionFunction::Parameter( u"radius"_s )
9666 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9667 fcnMakeCircle,
9668 u"GeometryGroup"_s
9669 )
9670 << new QgsStaticExpressionFunction(
9671 u"make_ellipse"_s,
9673 << QgsExpressionFunction::Parameter( u"center"_s )
9674 << QgsExpressionFunction::Parameter( u"semi_major_axis"_s )
9675 << QgsExpressionFunction::Parameter( u"semi_minor_axis"_s )
9676 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9677 << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ),
9678 fcnMakeEllipse,
9679 u"GeometryGroup"_s
9680 )
9681 << new QgsStaticExpressionFunction(
9682 u"make_regular_polygon"_s,
9684 << QgsExpressionFunction::Parameter( u"center"_s )
9685 << QgsExpressionFunction::Parameter( u"radius"_s )
9686 << QgsExpressionFunction::Parameter( u"number_sides"_s )
9687 << QgsExpressionFunction::Parameter( u"circle"_s, true, 0 ),
9688 fcnMakeRegularPolygon,
9689 u"GeometryGroup"_s
9690 )
9691 << new QgsStaticExpressionFunction( u"make_square"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"point1"_s ) << QgsExpressionFunction::Parameter( u"point2"_s ), fcnMakeSquare, u"GeometryGroup"_s )
9692 << new QgsStaticExpressionFunction(
9693 u"make_rectangle_3points"_s,
9695 << QgsExpressionFunction::Parameter( u"point1"_s )
9696 << QgsExpressionFunction::Parameter( u"point2"_s )
9697 << QgsExpressionFunction::Parameter( u"point3"_s )
9698 << QgsExpressionFunction::Parameter( u"option"_s, true, 0 ),
9699 fcnMakeRectangleFrom3Points,
9700 u"GeometryGroup"_s
9701 )
9702 << new QgsStaticExpressionFunction(
9703 u"make_valid"_s,
9705 QgsExpressionFunction::Parameter( u"geometry"_s ),
9706#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
9707 QgsExpressionFunction::Parameter( u"method"_s, true, u"linework"_s ),
9708#else
9709 QgsExpressionFunction::Parameter( u"method"_s, true, u"structure"_s ),
9710#endif
9711 QgsExpressionFunction::Parameter( u"keep_collapsed"_s, true, false )
9712 },
9713 fcnGeomMakeValid,
9714 u"GeometryGroup"_s
9715 );
9716
9717 functions
9718 << new QgsStaticExpressionFunction( u"x_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnXat, u"GeometryGroup"_s );
9719 functions
9720 << new QgsStaticExpressionFunction( u"y_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s, true ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnYat, u"GeometryGroup"_s );
9721 functions
9722 << new QgsStaticExpressionFunction( u"z_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnZat, u"GeometryGroup"_s );
9723 functions
9724 << new QgsStaticExpressionFunction( u"m_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s, true ), fcnMat, u"GeometryGroup"_s );
9725
9726 QgsStaticExpressionFunction *xAtFunc
9727 = 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 );
9728 xAtFunc->setIsStatic( false );
9729 functions << xAtFunc;
9730
9731
9732 QgsStaticExpressionFunction *yAtFunc
9733 = 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 );
9734 yAtFunc->setIsStatic( false );
9735 functions << yAtFunc;
9736
9737 functions
9738 << new QgsStaticExpressionFunction( u"geometry_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeometryType, u"GeometryGroup"_s )
9739 << 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 )
9740 << 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 )
9741 << 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 )
9742 << 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 )
9743 << 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 )
9744 << new QgsStaticExpressionFunction( u"geom_from_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary"_s ), fcnGeomFromWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
9745 << 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 )
9746 << new QgsStaticExpressionFunction( u"flip_coordinates"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnFlipCoordinates, u"GeometryGroup"_s )
9747 << new QgsStaticExpressionFunction( u"relate"_s, -1, fcnRelate, u"GeometryGroup"_s )
9748 << new QgsStaticExpressionFunction(
9749 u"intersects_bbox"_s,
9750 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
9751 fcnBbox,
9752 u"GeometryGroup"_s,
9753 QString(),
9754 false,
9755 QSet<QString>(),
9756 false,
9757 QStringList() << u"bbox"_s
9758 )
9759 << new QgsStaticExpressionFunction( u"disjoint"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDisjoint, u"GeometryGroup"_s )
9760 << new QgsStaticExpressionFunction( u"intersects"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersects, u"GeometryGroup"_s )
9761 << new QgsStaticExpressionFunction( u"touches"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnTouches, u"GeometryGroup"_s )
9762 << new QgsStaticExpressionFunction( u"crosses"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCrosses, u"GeometryGroup"_s )
9763 << new QgsStaticExpressionFunction( u"contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnContains, u"GeometryGroup"_s )
9764 << new QgsStaticExpressionFunction( u"overlaps"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnOverlaps, u"GeometryGroup"_s )
9765 << new QgsStaticExpressionFunction( u"within"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnWithin, u"GeometryGroup"_s )
9766 << new QgsStaticExpressionFunction( u"equals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnEquals, u"GeometryGroup"_s )
9767 << 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 )
9768 << new QgsStaticExpressionFunction(
9769 u"rotate"_s,
9771 << QgsExpressionFunction::Parameter( u"geometry"_s )
9772 << QgsExpressionFunction::Parameter( u"rotation"_s )
9773 << QgsExpressionFunction::Parameter( u"center"_s, true )
9774 << QgsExpressionFunction::Parameter( u"per_part"_s, true, false ),
9775 fcnRotate,
9776 u"GeometryGroup"_s
9777 )
9778 << new QgsStaticExpressionFunction(
9779 u"scale"_s,
9781 << QgsExpressionFunction::Parameter( u"geometry"_s )
9782 << QgsExpressionFunction::Parameter( u"x_scale"_s )
9783 << QgsExpressionFunction::Parameter( u"y_scale"_s )
9784 << QgsExpressionFunction::Parameter( u"center"_s, true ),
9785 fcnScale,
9786 u"GeometryGroup"_s
9787 )
9788 << new QgsStaticExpressionFunction(
9789 u"affine_transform"_s,
9791 << QgsExpressionFunction::Parameter( u"geometry"_s )
9792 << QgsExpressionFunction::Parameter( u"delta_x"_s )
9793 << QgsExpressionFunction::Parameter( u"delta_y"_s )
9794 << QgsExpressionFunction::Parameter( u"rotation_z"_s )
9795 << QgsExpressionFunction::Parameter( u"scale_x"_s )
9796 << QgsExpressionFunction::Parameter( u"scale_y"_s )
9797 << QgsExpressionFunction::Parameter( u"delta_z"_s, true, 0 )
9798 << QgsExpressionFunction::Parameter( u"delta_m"_s, true, 0 )
9799 << QgsExpressionFunction::Parameter( u"scale_z"_s, true, 1 )
9800 << QgsExpressionFunction::Parameter( u"scale_m"_s, true, 1 ),
9801 fcnAffineTransform,
9802 u"GeometryGroup"_s
9803 )
9804 << new QgsStaticExpressionFunction(
9805 u"buffer"_s,
9807 << QgsExpressionFunction::Parameter( u"geometry"_s )
9808 << QgsExpressionFunction::Parameter( u"distance"_s )
9809 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8 )
9810 << QgsExpressionFunction::Parameter( u"cap"_s, true, u"round"_s )
9811 << QgsExpressionFunction::Parameter( u"join"_s, true, u"round"_s )
9812 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2 ),
9813 fcnBuffer,
9814 u"GeometryGroup"_s
9815 )
9816 << new QgsStaticExpressionFunction( u"force_rhr"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForceRHR, u"GeometryGroup"_s )
9817 << new QgsStaticExpressionFunction( u"force_polygon_cw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCW, u"GeometryGroup"_s )
9818 << new QgsStaticExpressionFunction( u"force_polygon_ccw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnForcePolygonCCW, u"GeometryGroup"_s )
9819 << new QgsStaticExpressionFunction(
9820 u"wedge_buffer"_s,
9822 << QgsExpressionFunction::Parameter( u"center"_s )
9823 << QgsExpressionFunction::Parameter( u"azimuth"_s )
9824 << QgsExpressionFunction::Parameter( u"width"_s )
9825 << QgsExpressionFunction::Parameter( u"outer_radius"_s )
9826 << QgsExpressionFunction::Parameter( u"inner_radius"_s, true, 0.0 ),
9827 fcnWedgeBuffer,
9828 u"GeometryGroup"_s
9829 )
9830 << new QgsStaticExpressionFunction(
9831 u"tapered_buffer"_s,
9833 << QgsExpressionFunction::Parameter( u"geometry"_s )
9834 << QgsExpressionFunction::Parameter( u"start_width"_s )
9835 << QgsExpressionFunction::Parameter( u"end_width"_s )
9836 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 ),
9837 fcnTaperedBuffer,
9838 u"GeometryGroup"_s
9839 )
9840 << 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 )
9841 << new QgsStaticExpressionFunction(
9842 u"offset_curve"_s,
9844 << QgsExpressionFunction::Parameter( u"geometry"_s )
9845 << QgsExpressionFunction::Parameter( u"distance"_s )
9846 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9847 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
9848 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
9849 fcnOffsetCurve,
9850 u"GeometryGroup"_s
9851 )
9852 << new QgsStaticExpressionFunction(
9853 u"single_sided_buffer"_s,
9855 << QgsExpressionFunction::Parameter( u"geometry"_s )
9856 << QgsExpressionFunction::Parameter( u"distance"_s )
9857 << QgsExpressionFunction::Parameter( u"segments"_s, true, 8.0 )
9858 << QgsExpressionFunction::Parameter( u"join"_s, true, static_cast< int >( Qgis::JoinStyle::Round ) )
9859 << QgsExpressionFunction::Parameter( u"miter_limit"_s, true, 2.0 ),
9860 fcnSingleSidedBuffer,
9861 u"GeometryGroup"_s
9862 )
9863 << new QgsStaticExpressionFunction(
9864 u"extend"_s,
9865 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
9866 fcnExtend,
9867 u"GeometryGroup"_s
9868 )
9869 << new QgsStaticExpressionFunction( u"centroid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCentroid, u"GeometryGroup"_s )
9870 << new QgsStaticExpressionFunction( u"point_on_surface"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnPointOnSurface, u"GeometryGroup"_s )
9871 << new QgsStaticExpressionFunction( u"pole_of_inaccessibility"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnPoleOfInaccessibility, u"GeometryGroup"_s )
9872 << new QgsStaticExpressionFunction( u"reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnReverse, { u"String"_s, u"GeometryGroup"_s } )
9873 << new QgsStaticExpressionFunction( u"exterior_ring"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnExteriorRing, u"GeometryGroup"_s )
9874 << new QgsStaticExpressionFunction( u"interior_ring_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnInteriorRingN, u"GeometryGroup"_s )
9875 << new QgsStaticExpressionFunction( u"geometry_n"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"index"_s ), fcnGeometryN, u"GeometryGroup"_s )
9876 << new QgsStaticExpressionFunction( u"boundary"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundary, u"GeometryGroup"_s )
9877 << new QgsStaticExpressionFunction( u"line_merge"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnLineMerge, u"GeometryGroup"_s )
9878 << new QgsStaticExpressionFunction( u"shared_paths"_s, QgsExpressionFunction::ParameterList { QgsExpressionFunction::Parameter( u"geometry1"_s ), QgsExpressionFunction::Parameter( u"geometry2"_s ) }, fcnSharedPaths, u"GeometryGroup"_s )
9879 << new QgsStaticExpressionFunction( u"bounds"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBounds, u"GeometryGroup"_s )
9880 << new QgsStaticExpressionFunction( u"simplify"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplify, u"GeometryGroup"_s )
9881 << new QgsStaticExpressionFunction( u"simplify_vw"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"tolerance"_s ), fcnSimplifyVW, u"GeometryGroup"_s )
9882 << new QgsStaticExpressionFunction(
9883 u"smooth"_s,
9885 << QgsExpressionFunction::Parameter( u"geometry"_s )
9886 << QgsExpressionFunction::Parameter( u"iterations"_s, true, 1 )
9887 << QgsExpressionFunction::Parameter( u"offset"_s, true, 0.25 )
9888 << QgsExpressionFunction::Parameter( u"min_length"_s, true, -1 )
9889 << QgsExpressionFunction::Parameter( u"max_angle"_s, true, 180 ),
9890 fcnSmooth,
9891 u"GeometryGroup"_s
9892 )
9893 << new QgsStaticExpressionFunction(
9894 u"triangular_wave"_s,
9895 { QgsExpressionFunction::Parameter( u"geometry"_s ),
9896 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9897 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9898 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
9899 fcnTriangularWave,
9900 u"GeometryGroup"_s
9901 )
9902 << new QgsStaticExpressionFunction(
9903 u"triangular_wave_randomized"_s,
9904 { QgsExpressionFunction::Parameter( u"geometry"_s ),
9905 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9906 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9907 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9908 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9909 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
9910 fcnTriangularWaveRandomized,
9911 u"GeometryGroup"_s
9912 )
9913 << new QgsStaticExpressionFunction(
9914 u"square_wave"_s,
9915 { QgsExpressionFunction::Parameter( u"geometry"_s ),
9916 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9917 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9918 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
9919 fcnSquareWave,
9920 u"GeometryGroup"_s
9921 )
9922 << new QgsStaticExpressionFunction(
9923 u"square_wave_randomized"_s,
9924 { QgsExpressionFunction::Parameter( u"geometry"_s ),
9925 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9926 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9927 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9928 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9929 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
9930 fcnSquareWaveRandomized,
9931 u"GeometryGroup"_s
9932 )
9933 << new QgsStaticExpressionFunction(
9934 u"wave"_s,
9935 { QgsExpressionFunction::Parameter( u"geometry"_s ),
9936 QgsExpressionFunction::Parameter( u"wavelength"_s ),
9937 QgsExpressionFunction::Parameter( u"amplitude"_s ),
9938 QgsExpressionFunction::Parameter( u"strict"_s, true, false ) },
9939 fcnRoundWave,
9940 u"GeometryGroup"_s
9941 )
9942 << new QgsStaticExpressionFunction(
9943 u"wave_randomized"_s,
9944 { QgsExpressionFunction::Parameter( u"geometry"_s ),
9945 QgsExpressionFunction::Parameter( u"min_wavelength"_s ),
9946 QgsExpressionFunction::Parameter( u"max_wavelength"_s ),
9947 QgsExpressionFunction::Parameter( u"min_amplitude"_s ),
9948 QgsExpressionFunction::Parameter( u"max_amplitude"_s ),
9949 QgsExpressionFunction::Parameter( u"seed"_s, true, 0 ) },
9950 fcnRoundWaveRandomized,
9951 u"GeometryGroup"_s
9952 )
9953 << new QgsStaticExpressionFunction(
9954 u"apply_dash_pattern"_s,
9955 {
9956 QgsExpressionFunction::Parameter( u"geometry"_s ),
9957 QgsExpressionFunction::Parameter( u"pattern"_s ),
9958 QgsExpressionFunction::Parameter( u"start_rule"_s, true, u"no_rule"_s ),
9959 QgsExpressionFunction::Parameter( u"end_rule"_s, true, u"no_rule"_s ),
9960 QgsExpressionFunction::Parameter( u"adjustment"_s, true, u"both"_s ),
9961 QgsExpressionFunction::Parameter( u"pattern_offset"_s, true, 0 ),
9962 },
9963 fcnApplyDashPattern,
9964 u"GeometryGroup"_s
9965 )
9966 << new QgsStaticExpressionFunction( u"densify_by_count"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"vertices"_s ) }, fcnDensifyByCount, u"GeometryGroup"_s )
9967 << new QgsStaticExpressionFunction( u"densify_by_distance"_s, { QgsExpressionFunction::Parameter( u"geometry"_s ), QgsExpressionFunction::Parameter( u"distance"_s ) }, fcnDensifyByDistance, u"GeometryGroup"_s )
9968 << new QgsStaticExpressionFunction( u"num_points"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumPoints, u"GeometryGroup"_s )
9969 << new QgsStaticExpressionFunction( u"num_interior_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumInteriorRings, u"GeometryGroup"_s )
9970 << new QgsStaticExpressionFunction( u"num_rings"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumRings, u"GeometryGroup"_s )
9971 << new QgsStaticExpressionFunction( u"num_geometries"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomNumGeometries, u"GeometryGroup"_s )
9972 << new QgsStaticExpressionFunction( u"bounds_width"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsWidth, u"GeometryGroup"_s )
9973 << new QgsStaticExpressionFunction( u"bounds_height"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnBoundsHeight, u"GeometryGroup"_s )
9974 << new QgsStaticExpressionFunction( u"is_closed"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsClosed, u"GeometryGroup"_s )
9975 << new QgsStaticExpressionFunction( u"close_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnCloseLine, u"GeometryGroup"_s )
9976 << new QgsStaticExpressionFunction( u"is_empty"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnIsEmpty, u"GeometryGroup"_s )
9977 << new QgsStaticExpressionFunction(
9978 u"is_empty_or_null"_s,
9979 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ),
9980 fcnIsEmptyOrNull,
9981 u"GeometryGroup"_s,
9982 QString(),
9983 false,
9984 QSet<QString>(),
9985 false,
9986 QStringList(),
9987 true
9988 )
9989 << 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 )
9990#if GEOS_VERSION_MAJOR > 3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 11 )
9991 << new QgsStaticExpressionFunction(
9992 u"concave_hull"_s,
9994 << QgsExpressionFunction::Parameter( u"geometry"_s )
9995 << QgsExpressionFunction::Parameter( u"target_percent"_s )
9996 << QgsExpressionFunction::Parameter( u"allow_holes"_s, true, false ),
9997 fcnConcaveHull,
9998 u"GeometryGroup"_s
9999 )
10000#endif
10001 << new QgsStaticExpressionFunction( u"oriented_bbox"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnOrientedBBox, u"GeometryGroup"_s )
10002 << new QgsStaticExpressionFunction( u"main_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMainAngle, u"GeometryGroup"_s )
10003 << new QgsStaticExpressionFunction( u"minimal_circle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"segments"_s, true, 36 ), fcnMinimalCircle, u"GeometryGroup"_s )
10004 << new QgsStaticExpressionFunction( u"difference"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDifference, u"GeometryGroup"_s )
10005 << new QgsStaticExpressionFunction( u"distance"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnDistance, u"GeometryGroup"_s )
10006 << new QgsStaticExpressionFunction(
10007 u"hausdorff_distance"_s,
10009 << QgsExpressionFunction::Parameter( u"geometry1"_s )
10010 << QgsExpressionFunction::Parameter( u"geometry2"_s )
10011 << QgsExpressionFunction::Parameter( u"densify_fraction"_s, true ),
10012 fcnHausdorffDistance,
10013 u"GeometryGroup"_s
10014 )
10015 << new QgsStaticExpressionFunction( u"intersection"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnIntersection, u"GeometryGroup"_s )
10016 << new QgsStaticExpressionFunction(
10017 u"sym_difference"_s,
10018 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ),
10019 fcnSymDifference,
10020 u"GeometryGroup"_s,
10021 QString(),
10022 false,
10023 QSet<QString>(),
10024 false,
10025 QStringList() << u"symDifference"_s
10026 )
10027 << new QgsStaticExpressionFunction( u"combine"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10028 << new QgsStaticExpressionFunction( u"union"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnCombine, u"GeometryGroup"_s )
10029 << new QgsStaticExpressionFunction(
10030 u"geom_to_wkt"_s,
10031 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"precision"_s, true, 8.0 ),
10032 fcnGeomToWKT,
10033 u"GeometryGroup"_s,
10034 QString(),
10035 false,
10036 QSet<QString>(),
10037 false,
10038 QStringList() << u"geomToWKT"_s
10039 )
10040 << new QgsStaticExpressionFunction( u"geom_to_wkb"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomToWKB, u"GeometryGroup"_s, QString(), false, QSet<QString>(), false )
10041 << new QgsStaticExpressionFunction( u"geometry"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetGeometry, u"GeometryGroup"_s, QString(), true )
10042 << new QgsStaticExpressionFunction(
10043 u"transform"_s,
10044 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"source_auth_id"_s ) << QgsExpressionFunction::Parameter( u"dest_auth_id"_s ),
10045 fcnTransformGeometry,
10046 u"GeometryGroup"_s
10047 )
10048 << new QgsStaticExpressionFunction(
10049 u"extrude"_s,
10050 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"x"_s ) << QgsExpressionFunction::Parameter( u"y"_s ),
10051 fcnExtrude,
10052 u"GeometryGroup"_s,
10053 QString()
10054 )
10055 << new QgsStaticExpressionFunction( u"is_multipart"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnGeomIsMultipart, u"GeometryGroup"_s )
10056 << new QgsStaticExpressionFunction( u"z_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMax, u"GeometryGroup"_s )
10057 << new QgsStaticExpressionFunction( u"z_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnZMin, u"GeometryGroup"_s )
10058 << new QgsStaticExpressionFunction( u"m_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMax, u"GeometryGroup"_s )
10059 << new QgsStaticExpressionFunction( u"m_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnMMin, u"GeometryGroup"_s )
10060 << new QgsStaticExpressionFunction( u"sinuosity"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnSinuosity, u"GeometryGroup"_s )
10061 << new QgsStaticExpressionFunction( u"straight_distance_2d"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ), fcnStraightDistance2d, u"GeometryGroup"_s );
10062
10063
10064 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction(
10065 u"order_parts"_s,
10067 << QgsExpressionFunction::Parameter( u"geometry"_s )
10068 << QgsExpressionFunction::Parameter( u"orderby"_s )
10069 << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ),
10070 fcnOrderParts,
10071 u"GeometryGroup"_s,
10072 QString()
10073 );
10074
10075 orderPartsFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10076 const QList< QgsExpressionNode *> argList = node->args()->list();
10077 for ( QgsExpressionNode *argNode : argList )
10078 {
10079 if ( !argNode->isStatic( parent, context ) )
10080 return false;
10081 }
10082
10083 if ( node->args()->count() > 1 )
10084 {
10085 QgsExpressionNode *argNode = node->args()->at( 1 );
10086
10087 QString expString = argNode->eval( parent, context ).toString();
10088
10089 QgsExpression e( expString );
10090
10091 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10092 return true;
10093 }
10094
10095 return true;
10096 } );
10097
10098 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10099 if ( node->args()->count() > 1 )
10100 {
10101 QgsExpressionNode *argNode = node->args()->at( 1 );
10102 QString expression = argNode->eval( parent, context ).toString();
10104 e.prepare( context );
10105 context->setCachedValue( expression, QVariant::fromValue( e ) );
10106 }
10107 return true;
10108 } );
10109 functions << orderPartsFunc;
10110
10111 functions
10112 << new QgsStaticExpressionFunction( u"closest_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnClosestPoint, u"GeometryGroup"_s )
10113 << new QgsStaticExpressionFunction( u"shortest_line"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry1"_s ) << QgsExpressionFunction::Parameter( u"geometry2"_s ), fcnShortestLine, u"GeometryGroup"_s )
10114 << new QgsStaticExpressionFunction( u"line_interpolate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolatePoint, u"GeometryGroup"_s )
10115 << new QgsStaticExpressionFunction(
10116 u"line_interpolate_point_by_m"_s,
10118 << QgsExpressionFunction::Parameter( u"geometry"_s )
10119 << QgsExpressionFunction::Parameter( u"m"_s )
10120 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10121 fcnLineInterpolatePointByM,
10122 u"GeometryGroup"_s
10123 )
10124 << new QgsStaticExpressionFunction( u"line_interpolate_angle"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"distance"_s ), fcnLineInterpolateAngle, u"GeometryGroup"_s )
10125 << new QgsStaticExpressionFunction( u"line_locate_point"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"point"_s ), fcnLineLocatePoint, u"GeometryGroup"_s )
10126 << new QgsStaticExpressionFunction(
10127 u"line_locate_m"_s,
10129 << QgsExpressionFunction::Parameter( u"geometry"_s )
10130 << QgsExpressionFunction::Parameter( u"m"_s )
10131 << QgsExpressionFunction::Parameter( u"use_3d_distance"_s, true, false ),
10132 fcnLineLocateM,
10133 u"GeometryGroup"_s
10134 )
10135 << new QgsStaticExpressionFunction( u"angle_at_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnAngleAtVertex, u"GeometryGroup"_s )
10136 << new QgsStaticExpressionFunction( u"distance_to_vertex"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"vertex"_s ), fcnDistanceToVertex, u"GeometryGroup"_s )
10137 << new QgsStaticExpressionFunction(
10138 u"line_substring"_s,
10139 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometry"_s ) << QgsExpressionFunction::Parameter( u"start_distance"_s ) << QgsExpressionFunction::Parameter( u"end_distance"_s ),
10140 fcnLineSubset,
10141 u"GeometryGroup"_s
10142 );
10143
10144
10145 // **Record** functions
10146
10147 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( u"$id"_s, 0, fcnFeatureId, u"Record and Attributes"_s );
10148 idFunc->setIsStatic( false );
10149 functions << idFunc;
10150
10151 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( u"$currentfeature"_s, 0, fcnFeature, u"Record and Attributes"_s );
10152 currentFeatureFunc->setIsStatic( false );
10153 functions << currentFeatureFunc;
10154
10155 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction(
10156 u"uuid"_s,
10157 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"format"_s, true, u"WithBraces"_s ),
10158 fcnUuid,
10159 u"Record and Attributes"_s,
10160 QString(),
10161 false,
10162 QSet<QString>(),
10163 false,
10164 QStringList() << u"$uuid"_s
10165 );
10166 uuidFunc->setIsStatic( false );
10167 functions << uuidFunc;
10168
10169 functions
10170 << new QgsStaticExpressionFunction( u"feature_id"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s ), fcnGetFeatureId, u"Record and Attributes"_s, QString(), true )
10171 << new QgsStaticExpressionFunction(
10172 u"get_feature"_s,
10173 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"attribute"_s ) << QgsExpressionFunction::Parameter( u"value"_s, true ),
10174 fcnGetFeature,
10175 u"Record and Attributes"_s,
10176 QString(),
10177 false,
10178 QSet<QString>(),
10179 false,
10180 QStringList() << u"QgsExpressionUtils::getFeature"_s
10181 )
10182 << new QgsStaticExpressionFunction(
10183 u"get_feature_by_id"_s,
10184 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"feature_id"_s ),
10185 fcnGetFeatureById,
10186 u"Record and Attributes"_s,
10187 QString(),
10188 false,
10189 QSet<QString>(),
10190 false
10191 );
10192
10193 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction(
10194 u"attributes"_s,
10195 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"feature"_s, true ),
10196 fcnAttributes,
10197 u"Record and Attributes"_s,
10198 QString(),
10199 false,
10200 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10201 );
10202 attributesFunc->setIsStatic( false );
10203 functions << attributesFunc;
10204 QgsStaticExpressionFunction *representAttributesFunc
10205 = new QgsStaticExpressionFunction( u"represent_attributes"_s, -1, fcnRepresentAttributes, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10206 representAttributesFunc->setIsStatic( false );
10207 functions << representAttributesFunc;
10208
10209 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction(
10210 u"is_feature_valid"_s,
10212 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10213 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10214 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10215 fcnValidateFeature,
10216 u"Record and Attributes"_s,
10217 QString(),
10218 false,
10219 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10220 );
10221 validateFeature->setIsStatic( false );
10222 functions << validateFeature;
10223
10224 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction(
10225 u"is_attribute_valid"_s,
10227 << QgsExpressionFunction::Parameter( u"attribute"_s, false )
10229 << QgsExpressionFunction::Parameter( u"layer"_s, true )
10230 << QgsExpressionFunction::Parameter( u"feature"_s, true )
10231 << QgsExpressionFunction::Parameter( u"strength"_s, true ),
10232 fcnValidateAttribute,
10233 u"Record and Attributes"_s,
10234 QString(),
10235 false,
10236 QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES
10237 );
10238 validateAttribute->setIsStatic( false );
10239 functions << validateAttribute;
10240
10241 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction( u"maptip"_s, -1, fcnFeatureMaptip, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10242 maptipFunc->setIsStatic( false );
10243 functions << maptipFunc;
10244
10245 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction( u"display_expression"_s, -1, fcnFeatureDisplayExpression, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10246 displayFunc->setIsStatic( false );
10247 functions << displayFunc;
10248
10249 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction( u"is_selected"_s, -1, fcnIsSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10250 isSelectedFunc->setIsStatic( false );
10251 functions << isSelectedFunc;
10252
10253 functions << new QgsStaticExpressionFunction( u"num_selected"_s, -1, fcnNumSelected, u"Record and Attributes"_s, QString(), false, QSet<QString>() );
10254
10255 functions << new QgsStaticExpressionFunction(
10256 u"sqlite_fetch_and_increment"_s,
10258 << QgsExpressionFunction::Parameter( u"database"_s )
10259 << QgsExpressionFunction::Parameter( u"table"_s )
10260 << QgsExpressionFunction::Parameter( u"id_field"_s )
10261 << QgsExpressionFunction::Parameter( u"filter_attribute"_s )
10262 << QgsExpressionFunction::Parameter( u"filter_value"_s )
10263 << QgsExpressionFunction::Parameter( u"default_values"_s, true ),
10264 fcnSqliteFetchAndIncrement,
10265 u"Record and Attributes"_s
10266 );
10267
10268 // **CRS** functions
10269 functions
10270 << new QgsStaticExpressionFunction( u"crs_to_authid"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"crs"_s ), fcnCrsToAuthid, u"CRS"_s, QString(), true )
10271 << new QgsStaticExpressionFunction( u"crs_from_text"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"definition"_s ), fcnCrsFromText, u"CRS"_s );
10272
10273
10274 // **Fields and Values** functions
10275 QgsStaticExpressionFunction *representValueFunc
10276 = 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 );
10277
10278 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10279 Q_UNUSED( context )
10280 if ( node->args()->count() == 1 )
10281 {
10282 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
10283 if ( colRef )
10284 {
10285 return true;
10286 }
10287 else
10288 {
10289 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
10290 return false;
10291 }
10292 }
10293 else if ( node->args()->count() == 2 )
10294 {
10295 return true;
10296 }
10297 else
10298 {
10299 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
10300 return false;
10301 }
10302 } );
10303
10304 functions << representValueFunc;
10305
10306 // **General** functions
10307 functions
10308 << new QgsStaticExpressionFunction( u"layer_property"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"property"_s ), fcnGetLayerProperty, u"Map Layers"_s )
10309 << new QgsStaticExpressionFunction( u"decode_uri"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"part"_s, true ), fcnDecodeUri, u"Map Layers"_s )
10310 << new QgsStaticExpressionFunction( u"mime_type"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"binary_data"_s ), fcnMimeType, u"General"_s )
10311 << new QgsStaticExpressionFunction(
10312 u"raster_statistic"_s,
10313 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"statistic"_s ),
10314 fcnGetRasterBandStat,
10315 u"Rasters"_s
10316 );
10317
10318 // **var** function
10319 QgsStaticExpressionFunction *varFunction
10320 = new QgsStaticExpressionFunction( u"var"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnGetVariable, u"General"_s );
10321 varFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10322 /* A variable node is static if it has a static name and the name can be found at prepare
10323 * time and is tagged with isStatic.
10324 * It is not static if a variable is set during iteration or not tagged isStatic.
10325 * (e.g. geom_part variable)
10326 */
10327 if ( node->args()->count() > 0 )
10328 {
10329 QgsExpressionNode *argNode = node->args()->at( 0 );
10330
10331 if ( !argNode->isStatic( parent, context ) )
10332 return false;
10333
10334 const QString varName = argNode->eval( parent, context ).toString();
10335 if ( varName == "feature"_L1 || varName == "id"_L1 || varName == "geometry"_L1 )
10336 return false;
10337
10338 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
10339 return scope ? scope->isStatic( varName ) : false;
10340 }
10341 return false;
10342 } );
10343 varFunction->setUsesGeometryFunction( []( const QgsExpressionNodeFunction *node ) -> bool {
10344 if ( node && node->args()->count() > 0 )
10345 {
10346 QgsExpressionNode *argNode = node->args()->at( 0 );
10347 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
10348 {
10349 if ( literal->value() == "geometry"_L1 || literal->value() == "feature"_L1 )
10350 return true;
10351 }
10352 }
10353 return false;
10354 } );
10355
10356 functions << varFunction;
10357
10358 QgsStaticExpressionFunction *evalTemplateFunction
10359 = new QgsStaticExpressionFunction( u"eval_template"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"template"_s ), fcnEvalTemplate, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10360 evalTemplateFunction->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10361 if ( node->args()->count() > 0 )
10362 {
10363 QgsExpressionNode *argNode = node->args()->at( 0 );
10364
10365 if ( argNode->isStatic( parent, context ) )
10366 {
10367 QString expString = argNode->eval( parent, context ).toString();
10368
10369 QgsExpression e( expString );
10370
10371 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10372 return true;
10373 }
10374 }
10375
10376 return false;
10377 } );
10378 functions << evalTemplateFunction;
10379
10380 QgsStaticExpressionFunction *evalFunc
10381 = new QgsStaticExpressionFunction( u"eval"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"expression"_s ), fcnEval, u"General"_s, QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10382 evalFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10383 if ( node->args()->count() > 0 )
10384 {
10385 QgsExpressionNode *argNode = node->args()->at( 0 );
10386
10387 if ( argNode->isStatic( parent, context ) )
10388 {
10389 QString expString = argNode->eval( parent, context ).toString();
10390
10391 QgsExpression e( expString );
10392
10393 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
10394 return true;
10395 }
10396 }
10397
10398 return false;
10399 } );
10400
10401 functions << evalFunc;
10402
10403 QgsStaticExpressionFunction *attributeFunc
10404 = new QgsStaticExpressionFunction( u"attribute"_s, -1, fcnAttribute, u"Record and Attributes"_s, QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
10405 attributeFunc->setIsStaticFunction( []( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) {
10406 const QList< QgsExpressionNode *> argList = node->args()->list();
10407 for ( QgsExpressionNode *argNode : argList )
10408 {
10409 if ( !argNode->isStatic( parent, context ) )
10410 return false;
10411 }
10412
10413 if ( node->args()->count() == 1 )
10414 {
10415 // not static -- this is the variant which uses the current feature taken direct from the expression context
10416 return false;
10417 }
10418
10419 return true;
10420 } );
10421 functions << attributeFunc;
10422
10423 functions
10424 << new QgsStaticExpressionFunction( u"env"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ), fcnEnvVar, u"General"_s, QString() )
10425 << new QgsWithVariableExpressionFunction()
10426 << new QgsStaticExpressionFunction(
10427 u"raster_value"_s,
10428 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10429 fcnRasterValue,
10430 u"Rasters"_s
10431 )
10432 << new QgsStaticExpressionFunction(
10433 u"raster_attributes"_s,
10434 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"layer"_s ) << QgsExpressionFunction::Parameter( u"band"_s ) << QgsExpressionFunction::Parameter( u"point"_s ),
10435 fcnRasterAttributes,
10436 u"Rasters"_s
10437 )
10438
10439 // functions for arrays
10440 << new QgsArrayForeachExpressionFunction()
10441 << new QgsArrayFilterExpressionFunction()
10442 << new QgsStaticExpressionFunction( u"array"_s, -1, fcnArray, u"Arrays"_s, QString(), false, QSet<QString>(), false, QStringList(), true )
10443 << new QgsStaticExpressionFunction( u"array_sort"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"ascending"_s, true, true ), fcnArraySort, u"Arrays"_s )
10444 << new QgsStaticExpressionFunction( u"array_length"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLength, u"Arrays"_s )
10445 << new QgsStaticExpressionFunction( u"array_contains"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayContains, u"Arrays"_s )
10446 << new QgsStaticExpressionFunction( u"array_count"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayCount, u"Arrays"_s )
10447 << new QgsStaticExpressionFunction( u"array_all"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array_a"_s ) << QgsExpressionFunction::Parameter( u"array_b"_s ), fcnArrayAll, u"Arrays"_s )
10448 << new QgsStaticExpressionFunction( u"array_find"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayFind, u"Arrays"_s )
10449 << new QgsStaticExpressionFunction( u"array_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayGet, u"Arrays"_s )
10450 << new QgsStaticExpressionFunction( u"array_first"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayFirst, u"Arrays"_s )
10451 << new QgsStaticExpressionFunction( u"array_last"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayLast, u"Arrays"_s )
10452 << new QgsStaticExpressionFunction( u"array_min"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMinimum, u"Arrays"_s )
10453 << new QgsStaticExpressionFunction( u"array_max"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMaximum, u"Arrays"_s )
10454 << new QgsStaticExpressionFunction( u"array_mean"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMean, u"Arrays"_s )
10455 << new QgsStaticExpressionFunction( u"array_median"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayMedian, u"Arrays"_s )
10456 << 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 )
10457 << 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 )
10458 << new QgsStaticExpressionFunction( u"array_sum"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArraySum, u"Arrays"_s )
10459 << new QgsStaticExpressionFunction( u"array_append"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayAppend, u"Arrays"_s )
10460 << new QgsStaticExpressionFunction( u"array_prepend"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ), fcnArrayPrepend, u"Arrays"_s )
10461 << new QgsStaticExpressionFunction(
10462 u"array_insert"_s,
10463 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10464 fcnArrayInsert,
10465 u"Arrays"_s
10466 )
10467 << new QgsStaticExpressionFunction( u"array_remove_at"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"pos"_s ), fcnArrayRemoveAt, u"Arrays"_s )
10468 << new QgsStaticExpressionFunction(
10469 u"array_remove_all"_s,
10470 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"value"_s ),
10471 fcnArrayRemoveAll,
10472 u"Arrays"_s,
10473 QString(),
10474 false,
10475 QSet<QString>(),
10476 false,
10477 QStringList(),
10478 true
10479 )
10480 << new QgsStaticExpressionFunction( u"array_replace"_s, -1, fcnArrayReplace, u"Arrays"_s )
10481 << new QgsStaticExpressionFunction( u"array_prioritize"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"array_prioritize"_s ), fcnArrayPrioritize, u"Arrays"_s )
10482 << new QgsStaticExpressionFunction( u"array_cat"_s, -1, fcnArrayCat, u"Arrays"_s )
10483 << new QgsStaticExpressionFunction(
10484 u"array_slice"_s,
10485 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"start_pos"_s ) << QgsExpressionFunction::Parameter( u"end_pos"_s ),
10486 fcnArraySlice,
10487 u"Arrays"_s
10488 )
10489 << new QgsStaticExpressionFunction( u"array_reverse"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayReverse, u"Arrays"_s )
10490 << new QgsStaticExpressionFunction( u"array_intersect"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array1"_s ) << QgsExpressionFunction::Parameter( u"array2"_s ), fcnArrayIntersect, u"Arrays"_s )
10491 << new QgsStaticExpressionFunction( u"array_distinct"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ), fcnArrayDistinct, u"Arrays"_s )
10492 << new QgsStaticExpressionFunction(
10493 u"array_to_string"_s,
10495 << QgsExpressionFunction::Parameter( u"array"_s )
10496 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10497 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10498 fcnArrayToString,
10499 u"Arrays"_s
10500 )
10501 << new QgsStaticExpressionFunction(
10502 u"string_to_array"_s,
10504 << QgsExpressionFunction::Parameter( u"string"_s )
10505 << QgsExpressionFunction::Parameter( u"delimiter"_s, true, "," )
10506 << QgsExpressionFunction::Parameter( u"emptyvalue"_s, true, "" ),
10507 fcnStringToArray,
10508 u"Arrays"_s
10509 )
10510 << new QgsStaticExpressionFunction(
10511 u"generate_series"_s,
10512 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"start"_s ) << QgsExpressionFunction::Parameter( u"stop"_s ) << QgsExpressionFunction::Parameter( u"step"_s, true, 1.0 ),
10513 fcnGenerateSeries,
10514 u"Arrays"_s
10515 )
10516 << new QgsStaticExpressionFunction( u"geometries_to_array"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"geometries"_s ), fcnGeometryCollectionAsArray, u"Arrays"_s )
10517
10518 //functions for maps
10519 << 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 )
10520 << 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 )
10521 << new QgsStaticExpressionFunction( u"hstore_to_map"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"string"_s ), fcnHstoreToMap, u"Maps"_s )
10522 << new QgsStaticExpressionFunction( u"map_to_hstore"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHstore, u"Maps"_s )
10523 << new QgsStaticExpressionFunction( u"map"_s, -1, fcnMap, u"Maps"_s )
10524 << new QgsStaticExpressionFunction( u"map_get"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapGet, u"Maps"_s )
10525 << new QgsStaticExpressionFunction( u"map_exist"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapExist, u"Maps"_s )
10526 << new QgsStaticExpressionFunction( u"map_delete"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"key"_s ), fcnMapDelete, u"Maps"_s )
10527 << 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 )
10528 << new QgsStaticExpressionFunction( u"map_concat"_s, -1, fcnMapConcat, u"Maps"_s )
10529 << new QgsStaticExpressionFunction( u"map_akeys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAKeys, u"Maps"_s )
10530 << new QgsStaticExpressionFunction( u"map_avals"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapAVals, u"Maps"_s )
10531 << new QgsStaticExpressionFunction( u"map_prefix_keys"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ) << QgsExpressionFunction::Parameter( u"prefix"_s ), fcnMapPrefixKeys, u"Maps"_s )
10532 << new QgsStaticExpressionFunction( u"map_to_html_table"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlTable, u"Maps"_s )
10533 << new QgsStaticExpressionFunction( u"map_to_html_dl"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnMapToHtmlDefinitionList, u"Maps"_s )
10534 << new QgsStaticExpressionFunction( u"url_encode"_s, QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"map"_s ), fcnToFormUrlEncode, u"Maps"_s )
10535
10536 ;
10537
10539
10540 //QgsExpression has ownership of all built-in functions
10541 for ( QgsExpressionFunction *func : std::as_const( functions ) )
10542 {
10543 *sOwnedFunctions() << func;
10544 *sBuiltinFunctions() << func->name();
10545 sBuiltinFunctions()->append( func->aliases() );
10546 }
10547 }
10548 return functions;
10549}
10550
10551bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
10552{
10553 int fnIdx = functionIndex( function->name() );
10554 if ( fnIdx != -1 )
10555 {
10556 return false;
10557 }
10558
10559 QMutexLocker locker( &sFunctionsMutex );
10560 sFunctions()->append( function );
10561 if ( transferOwnership )
10562 sOwnedFunctions()->append( function );
10563
10564 return true;
10565}
10566
10567bool QgsExpression::unregisterFunction( const QString &name )
10568{
10569 // You can never override the built in functions.
10570 if ( QgsExpression::BuiltinFunctions().contains( name ) )
10571 {
10572 return false;
10573 }
10574 int fnIdx = functionIndex( name );
10575 if ( fnIdx != -1 )
10576 {
10577 QMutexLocker locker( &sFunctionsMutex );
10578 sFunctions()->removeAt( fnIdx );
10579 sFunctionIndexMap.clear();
10580 return true;
10581 }
10582 return false;
10583}
10584
10586{
10587 const QList<QgsExpressionFunction *> &ownedFunctions = *sOwnedFunctions();
10588 for ( QgsExpressionFunction *func : std::as_const( ownedFunctions ) )
10589 {
10590 sBuiltinFunctions()->removeAll( func->name() );
10591 for ( const QString &alias : func->aliases() )
10593 sBuiltinFunctions()->removeAll( alias );
10594 }
10595
10596 sFunctions()->removeAll( func );
10597 }
10598
10599 qDeleteAll( *sOwnedFunctions() );
10600 sOwnedFunctions()->clear();
10602
10603const QStringList &QgsExpression::BuiltinFunctions()
10604{
10605 if ( sBuiltinFunctions()->isEmpty() )
10606 {
10607 Functions(); // this method builds the gmBuiltinFunctions as well
10608 }
10609 return *sBuiltinFunctions();
10610}
10611
10614 u"array_foreach"_s, // skip-keyword-check
10615 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"array"_s ) << QgsExpressionFunction::Parameter( u"expression"_s ),
10616 u"Arrays"_s
10617 )
10618{}
10619
10621{
10622 bool isStatic = false;
10623
10624 QgsExpressionNode::NodeList *args = node->args();
10626 if ( args->count() < 2 )
10627 return false;
10628
10629 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10630 {
10631 isStatic = true;
10632 }
10633 return isStatic;
10634}
10635
10637{
10638 Q_UNUSED( node )
10639 QVariantList result;
10640
10641 if ( args->count() < 2 )
10642 // error
10643 return result;
10644
10645 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10646
10647 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10648 std::unique_ptr< QgsExpressionContext > tempContext;
10649 if ( !subContext )
10650 {
10651 tempContext = std::make_unique< QgsExpressionContext >();
10652 subContext = tempContext.get();
10653 }
10654
10655 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10656 subContext->appendScope( subScope );
10657
10658 int i = 0;
10659 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
10660 {
10661 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, *it, true ) );
10662 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, i, true ) );
10663 result << args->at( 1 )->eval( parent, subContext );
10664 }
10665
10666 if ( context )
10667 delete subContext->popScope();
10668
10669 return result;
10670}
10671
10672QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10674 // This is a dummy function, all the real handling is in run
10675 Q_UNUSED( values )
10676 Q_UNUSED( context )
10677 Q_UNUSED( parent )
10678 Q_UNUSED( node )
10679
10680 Q_ASSERT( false );
10681 return QVariant();
10682}
10683
10685{
10686 QgsExpressionNode::NodeList *args = node->args();
10687
10688 if ( args->count() < 2 )
10689 // error
10690 return false;
10691
10692 args->at( 0 )->prepare( parent, context );
10693
10694 QgsExpressionContext subContext;
10695 if ( context )
10696 subContext = *context;
10699 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
10700 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"counter"_s, QVariant(), true ) );
10701 subContext.appendScope( subScope );
10702
10703 args->at( 1 )->prepare( parent, &subContext );
10704
10705 return true;
10706}
10707
10709 : 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 )
10710{}
10711
10713{
10714 bool isStatic = false;
10715
10716 QgsExpressionNode::NodeList *args = node->args();
10718 if ( args->count() < 2 )
10719 return false;
10720
10721 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10722 {
10723 isStatic = true;
10724 }
10725 return isStatic;
10726}
10727
10729{
10730 Q_UNUSED( node )
10731 QVariantList result;
10732
10733 if ( args->count() < 2 )
10734 // error
10735 return result;
10736
10737 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10738
10739 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10740 std::unique_ptr< QgsExpressionContext > tempContext;
10741 if ( !subContext )
10742 {
10743 tempContext = std::make_unique< QgsExpressionContext >();
10744 subContext = tempContext.get();
10745 }
10746
10747 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10748 subContext->appendScope( subScope );
10749
10750 int limit = 0;
10751 if ( args->count() >= 3 )
10752 {
10753 const QVariant limitVar = args->at( 2 )->eval( parent, context );
10754
10755 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
10756 {
10757 limit = limitVar.toInt();
10758 }
10759 else
10760 {
10761 return result;
10762 }
10763 }
10764
10765 for ( const QVariant &value : array )
10766 {
10767 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, value, true ) );
10768 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
10769 {
10770 result << value;
10771
10772 if ( limit > 0 && limit == result.size() )
10773 break;
10774 }
10775 }
10776
10777 if ( context )
10778 delete subContext->popScope();
10779
10780 return result;
10781}
10782
10783QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10785 // This is a dummy function, all the real handling is in run
10786 Q_UNUSED( values )
10787 Q_UNUSED( context )
10788 Q_UNUSED( parent )
10789 Q_UNUSED( node )
10790
10791 Q_ASSERT( false );
10792 return QVariant();
10793}
10794
10796{
10797 QgsExpressionNode::NodeList *args = node->args();
10798
10799 if ( args->count() < 2 )
10800 // error
10801 return false;
10802
10803 args->at( 0 )->prepare( parent, context );
10804
10805 QgsExpressionContext subContext;
10806 if ( context )
10807 subContext = *context;
10808
10810 subScope->addVariable( QgsExpressionContextScope::StaticVariable( u"element"_s, QVariant(), true ) );
10811 subContext.appendScope( subScope );
10812
10813 args->at( 1 )->prepare( parent, &subContext );
10814
10815 return true;
10816}
10818 : 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 )
10819{}
10820
10822{
10823 bool isStatic = false;
10824
10825 QgsExpressionNode::NodeList *args = node->args();
10826
10827 if ( args->count() < 3 )
10828 return false;
10829
10830 // We only need to check if the node evaluation is static, if both - name and value - are static.
10831 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10832 {
10833 QVariant name = args->at( 0 )->eval( parent, context );
10834 QVariant value = args->at( 1 )->eval( parent, context );
10836 // Temporarily append a new scope to provide the variable
10837 appendTemporaryVariable( context, name.toString(), value );
10838 if ( args->at( 2 )->isStatic( parent, context ) )
10839 isStatic = true;
10840 popTemporaryVariable( context );
10841 }
10842
10843 return isStatic;
10844}
10845
10847{
10848 Q_UNUSED( node )
10849 QVariant result;
10850
10851 if ( args->count() < 3 )
10852 // error
10853 return result;
10854
10855 QVariant name = args->at( 0 )->eval( parent, context );
10856 QVariant value = args->at( 1 )->eval( parent, context );
10857
10858 const QgsExpressionContext *updatedContext = context;
10859 std::unique_ptr< QgsExpressionContext > tempContext;
10860 if ( !updatedContext )
10861 {
10862 tempContext = std::make_unique< QgsExpressionContext >();
10863 updatedContext = tempContext.get();
10865
10866 appendTemporaryVariable( updatedContext, name.toString(), value );
10867 result = args->at( 2 )->eval( parent, updatedContext );
10868
10869 if ( context )
10870 popTemporaryVariable( updatedContext );
10871
10872 return result;
10873}
10874
10875QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10877 // This is a dummy function, all the real handling is in run
10878 Q_UNUSED( values )
10879 Q_UNUSED( context )
10880 Q_UNUSED( parent )
10881 Q_UNUSED( node )
10882
10883 Q_ASSERT( false );
10884 return QVariant();
10885}
10886
10888{
10889 QgsExpressionNode::NodeList *args = node->args();
10890
10891 if ( args->count() < 3 )
10892 // error
10893 return false;
10894
10895 QVariant name = args->at( 0 )->prepare( parent, context );
10896 QVariant value = args->at( 1 )->prepare( parent, context );
10897
10898 const QgsExpressionContext *updatedContext = context;
10899 std::unique_ptr< QgsExpressionContext > tempContext;
10900 if ( !updatedContext )
10901 {
10902 tempContext = std::make_unique< QgsExpressionContext >();
10903 updatedContext = tempContext.get();
10904 }
10905
10906 appendTemporaryVariable( updatedContext, name.toString(), value );
10907 args->at( 2 )->prepare( parent, updatedContext );
10908
10909 if ( context )
10910 popTemporaryVariable( updatedContext );
10911
10912 return true;
10913}
10914
10915void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10916{
10917 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10918 delete updatedContext->popScope();
10919}
10920
10921void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10922{
10923 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
10924 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
10925
10926 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10927 updatedContext->appendScope( scope );
10928}
@ Left
Buffer to left of line.
Definition qgis.h:2177
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3405
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3407
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3406
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3408
@ Success
Operation succeeded.
Definition qgis.h:2122
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3117
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2276
@ 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:2201
@ Bevel
Use beveled joins.
Definition qgis.h:2204
@ Round
Use rounded joins.
Definition qgis.h:2202
@ Miter
Use mitered joins.
Definition qgis.h:2203
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:6297
@ StdDev
Standard deviation.
Definition qgis.h:6304
@ NoStatistic
No statistic.
Definition qgis.h:6298
@ 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:2188
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2190
@ Round
Round cap.
Definition qgis.h:2189
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2191
Aggregate
Available aggregates to calculate.
Definition qgis.h:6174
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:6191
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:6188
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:6181
@ Median
Median of values (numeric fields only).
Definition qgis.h:6182
@ Max
Max of values.
Definition qgis.h:6179
@ Min
Min of values.
Definition qgis.h:6178
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:6192
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:6185
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:6196
@ Sum
Sum of values.
Definition qgis.h:6180
@ Minority
Minority of values.
Definition qgis.h:6186
@ CountMissing
Number of missing (null) values.
Definition qgis.h:6177
@ ArrayAggregate
Create an array of values.
Definition qgis.h:6195
@ Majority
Majority of values.
Definition qgis.h:6187
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:6184
@ Count
Count.
Definition qgis.h:6175
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:6189
@ CountDistinct
Number of distinct values.
Definition qgis.h:6176
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:6193
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:6194
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:6190
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3390
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3393
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3395
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3394
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3392
@ NoRule
No special rule.
Definition qgis.h:3391
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2260
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2261
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2262
@ Point
Point.
Definition qgis.h:296
@ PointM
PointM.
Definition qgis.h:329
@ PointZ
PointZ.
Definition qgis.h:313
@ GeometryCollection
GeometryCollection.
Definition qgis.h:303
@ PointZM
PointZM.
Definition qgis.h:345
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition qgscircle.h:46
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
QFlags< FormatFlag > FormatFlags
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition qgscurve.cpp:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:175
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:53
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:272
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:41
QString what() const
Contains utilities for working with EXIF tags in images.
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
An abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
QgsExpressionFunction(const QString &fnname, int params, const QString &group, const QString &helpText=QString(), bool lazyEval=false, bool handlesNull=false, bool isContextual=false)
Constructor for function which uses unnamed parameters.
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
const QString helpText() const
The help text for the function.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes its value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions).
friend class QgsExpressionNodeFunction
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsExpression(const QString &expr)
Creates a new expression based on the provided string.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:70
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
bool hasGeometry() const
Returns true if the feature has an associated geometry.
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:739
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
int numGeometries() const
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
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 difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up 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 triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
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.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
QgsGeometry 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 equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
Qgis::GeometryType type
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
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 intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
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) 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.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
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:139
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2796
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
A representation of the interval between two datetime values.
Definition qgsinterval.h:52
bool isValid() const
Returns true if the interval is valid.
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
double years() const
Returns the interval duration in years (based on an average year length).
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Represents a model of the Earth's magnetic field.
static bool fieldComponentsWithTimeDerivatives(double Bx, double By, double Bz, double Bxt, double Byt, double Bzt, double &H, double &F, double &D, double &I, double &Ht, double &Ft, double &Dt, double &It)
Compute various quantities dependent on a magnetic field and their rates of change.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QString name
Definition qgsmaplayer.h:87
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:86
QgsLayerMetadata metadata
Definition qgsmaplayer.h:89
Qgis::LayerType type
Definition qgsmaplayer.h:93
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString mapTipTemplate
Definition qgsmaplayer.h:96
Implementation of a geometry simplifier using the "MapToPixel" algorithm.
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:245
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:722
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:588
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:456
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:138
double z
Definition qgspoint.h:58
double x
Definition qgspoint.h:56
double m
Definition qgspoint.h:59
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:734
double y
Definition qgspoint.h:57
QgsRelationManager * relationManager
Definition qgsproject.h:124
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:501
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:148
Contains utility functions for working with symbols and symbol layers.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition qgis.cpp:596
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7504
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7503
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:7015
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
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)
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 *)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:98
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition qgsogcutils.h:63
const QgsMapLayer * layer
Definition qgsogcutils.h:71
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:72
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:34