QGIS API Documentation 3.43.0-Master (b60ef06885e)
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
6 email : matthias@opengis.ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
17#include <random>
18
20#include "qgscoordinateutils.h"
22#include "qgsexpressionutils.h"
24#include "qgsexiftools.h"
25#include "qgsfeaturerequest.h"
26#include "qgsgeos.h"
27#include "qgsstringutils.h"
28#include "qgsmultipoint.h"
29#include "qgsgeometryutils.h"
30#include "qgshstoreutils.h"
31#include "qgsmultilinestring.h"
32#include "qgslinestring.h"
33#include "qgscurvepolygon.h"
35#include "qgspolygon.h"
36#include "qgstriangle.h"
37#include "qgscurve.h"
38#include "qgsregularpolygon.h"
39#include "qgsquadrilateral.h"
40#include "qgsvariantutils.h"
41#include "qgsogcutils.h"
42#include "qgsdistancearea.h"
43#include "qgsgeometryengine.h"
45#include "qgssymbollayerutils.h"
46#include "qgsstyle.h"
47#include "qgsexception.h"
48#include "qgsmessagelog.h"
49#include "qgsrasterlayer.h"
50#include "qgsvectorlayer.h"
51#include "qgsvectorlayerutils.h"
52#include "qgsrasterbandstats.h"
53#include "qgscolorramp.h"
55#include "qgsfieldformatter.h"
57#include "qgsproviderregistry.h"
58#include "sqlite3.h"
59#include "qgstransaction.h"
60#include "qgsthreadingutils.h"
61#include "qgsapplication.h"
62#include "qgis.h"
64#include "qgsunittypes.h"
65#include "qgsspatialindex.h"
66#include "qgscolorrampimpl.h"
67
68#include <QMimeDatabase>
69#include <QProcessEnvironment>
70#include <QCryptographicHash>
71#include <QRegularExpression>
72#include <QUuid>
73#include <QUrlQuery>
74
75typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
76
78Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
80
83Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
84
85const QString QgsExpressionFunction::helpText() const
86{
87 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
88}
89
91{
92 Q_UNUSED( node )
93 // evaluate arguments
94 QVariantList argValues;
95 if ( args )
96 {
97 int arg = 0;
98 const QList< QgsExpressionNode * > argList = args->list();
99 for ( QgsExpressionNode *n : argList )
100 {
101 QVariant v;
102 if ( lazyEval() )
103 {
104 // Pass in the node for the function to eval as it needs.
105 v = QVariant::fromValue( n );
106 }
107 else
108 {
109 v = n->eval( parent, context );
111 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
112 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
113 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
114 }
115 argValues.append( v );
116 arg++;
117 }
118 }
119
120 return func( argValues, context, parent, node );
121}
122
124{
125 Q_UNUSED( node )
126 return true;
127}
128
130{
131 return QStringList();
132}
133
135{
136 Q_UNUSED( parent )
137 Q_UNUSED( context )
138 Q_UNUSED( node )
139 return false;
140}
141
143{
144 Q_UNUSED( parent )
145 Q_UNUSED( context )
146 Q_UNUSED( node )
147 return true;
148}
149
151{
152 Q_UNUSED( node )
153 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
154}
155
157{
158 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
159}
160
162{
163 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
164}
165
167{
168 return mHandlesNull;
169}
170
171// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
174 FcnEval fcn,
175 const QString &group,
176 const QString &helpText,
177 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
178 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
179 bool lazyEval,
180 const QStringList &aliases,
181 bool handlesNull )
182 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
183 , mFnc( fcn )
184 , mAliases( aliases )
185 , mUsesGeometry( false )
186 , mUsesGeometryFunc( usesGeometry )
187 , mReferencedColumnsFunc( referencedColumns )
188{
189}
191
193{
194 return mAliases;
195}
196
198{
199 if ( mUsesGeometryFunc )
200 return mUsesGeometryFunc( node );
201 else
202 return mUsesGeometry;
203}
204
205void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
206{
207 mUsesGeometryFunc = usesGeometry;
208}
209
211{
212 if ( mReferencedColumnsFunc )
213 return mReferencedColumnsFunc( node );
214 else
215 return mReferencedColumns;
216}
217
219{
220 if ( mIsStaticFunc )
221 return mIsStaticFunc( node, parent, context );
222 else
223 return mIsStatic;
224}
225
227{
228 if ( mPrepareFunc )
229 return mPrepareFunc( node, parent, context );
230
231 return true;
232}
233
235{
236 mIsStaticFunc = isStatic;
237}
238
240{
241 mIsStaticFunc = nullptr;
242 mIsStatic = isStatic;
243}
244
245void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
246{
247 mPrepareFunc = prepareFunc;
248}
249
251{
252 if ( node && node->args() )
253 {
254 const QList< QgsExpressionNode * > argList = node->args()->list();
255 for ( QgsExpressionNode *argNode : argList )
256 {
257 if ( !argNode->isStatic( parent, context ) )
258 return false;
259 }
260 }
261
262 return true;
263}
264
265static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
266{
267 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
268 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
269 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
270
271 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
272 return QVariant();
273
274 QVariantList array;
275 int length = 1;
276
277 array << start;
278 double current = start + step;
279 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
280 {
281 array << current;
282 current += step;
283 length++;
284 }
285
286 return array;
287}
288
289static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
290{
291 if ( !context )
292 return QVariant();
293
294 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
295
296 if ( name == QLatin1String( "feature" ) )
297 {
298 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
299 }
300 else if ( name == QLatin1String( "id" ) )
301 {
302 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
303 }
304 else if ( name == QLatin1String( "geometry" ) )
305 {
306 if ( !context->hasFeature() )
307 return QVariant();
308
309 const QgsFeature feature = context->feature();
310 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
311 }
312 else
313 {
314 return context->variable( name );
315 }
316}
317
318static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
319{
320 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
321 return QgsExpression::replaceExpressionText( templateString, context );
322}
323
324static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
325{
326 if ( !context )
327 return QVariant();
328
329 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
330 QgsExpression expression( expString );
331 return expression.evaluate( context );
332}
333
334static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
335{
336 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
337 return QVariant( std::sqrt( x ) );
338}
339
340static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341{
342 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343 return QVariant( std::fabs( val ) );
344}
345
346static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
347{
348 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
349 return ( deg * M_PI ) / 180;
350}
351static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
352{
353 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
354 return ( 180 * rad ) / M_PI;
355}
356static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
357{
358 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
359 return QVariant( std::sin( x ) );
360}
361static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
362{
363 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
364 return QVariant( std::cos( x ) );
365}
366static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
367{
368 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
369 return QVariant( std::tan( x ) );
370}
371static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
372{
373 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
374 return QVariant( std::asin( x ) );
375}
376static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
377{
378 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
379 return QVariant( std::acos( x ) );
380}
381static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
382{
383 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
384 return QVariant( std::atan( x ) );
385}
386static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
387{
388 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
389 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
390 return QVariant( std::atan2( y, x ) );
391}
392static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
393{
394 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
395 return QVariant( std::exp( x ) );
396}
397static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
398{
399 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
400 if ( x <= 0 )
401 return QVariant();
402 return QVariant( std::log( x ) );
403}
404static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 if ( x <= 0 )
408 return QVariant();
409 return QVariant( log10( x ) );
410}
411static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
412{
413 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
414 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
415 if ( x <= 0 || b <= 0 )
416 return QVariant();
417 return QVariant( std::log( x ) / std::log( b ) );
418}
419static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
420{
421 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
422 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
423 if ( max < min )
424 return QVariant();
425
426 std::random_device rd;
427 std::mt19937_64 generator( rd() );
428
429 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
430 {
431 quint32 seed;
432 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
433 {
434 // if seed can be converted to int, we use as is
435 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
436 }
437 else
438 {
439 // if not, we hash string representation to int
440 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
441 std::hash<std::string> hasher;
442 seed = hasher( seedStr.toStdString() );
443 }
444 generator.seed( seed );
445 }
446
447 // Return a random double in the range [min, max] (inclusive)
448 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
449 return QVariant( min + f * ( max - min ) );
450}
451static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
452{
453 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
454 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
455 if ( max < min )
456 return QVariant();
457
458 std::random_device rd;
459 std::mt19937_64 generator( rd() );
460
461 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
462 {
463 quint32 seed;
464 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
465 {
466 // if seed can be converted to int, we use as is
467 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
468 }
469 else
470 {
471 // if not, we hash string representation to int
472 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
473 std::hash<std::string> hasher;
474 seed = hasher( seedStr.toStdString() );
475 }
476 generator.seed( seed );
477 }
478
479 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
480 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
481 return QVariant( randomInteger );
482
483 // Prevent wrong conversion of QVariant. See #36412
484 return QVariant( int( randomInteger ) );
485}
486
487static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
488{
489 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
490 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
491 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
492 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
493 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
494
495 if ( domainMin >= domainMax )
496 {
497 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
498 return QVariant();
499 }
500
501 // outside of domain?
502 if ( val >= domainMax )
503 {
504 return rangeMax;
505 }
506 else if ( val <= domainMin )
507 {
508 return rangeMin;
509 }
510
511 // calculate linear scale
512 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
513 double c = rangeMin - ( domainMin * m );
514
515 // Return linearly scaled value
516 return QVariant( m * val + c );
517}
518
519static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
520{
521 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
522 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
523 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
524 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
525 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
526 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
527
528 if ( domainMin >= domainMax )
529 {
530 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
531 return QVariant();
532 }
533 if ( exponent <= 0 )
534 {
535 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
536 return QVariant();
537 }
538
539 // outside of domain?
540 if ( val >= domainMax )
541 {
542 return rangeMax;
543 }
544 else if ( val <= domainMin )
545 {
546 return rangeMin;
547 }
548
549 // Return polynomially scaled value
550 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
551}
552
553static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
554{
555 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
556 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
557 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
558 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
559 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
560 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
561
562 if ( domainMin >= domainMax )
563 {
564 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
565 return QVariant();
566 }
567 if ( exponent <= 0 )
568 {
569 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
570 return QVariant();
571 }
572
573 // outside of domain?
574 if ( val >= domainMax )
575 {
576 return rangeMax;
577 }
578 else if ( val <= domainMin )
579 {
580 return rangeMin;
581 }
582
583 // Return exponentially scaled value
584 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
585 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
586}
587
588static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
589{
590 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
591 double maxVal = std::numeric_limits<double>::quiet_NaN();
592 for ( const QVariant &val : values )
593 {
594 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
595 if ( std::isnan( maxVal ) )
596 {
597 maxVal = testVal;
598 }
599 else if ( !std::isnan( testVal ) )
600 {
601 maxVal = std::max( maxVal, testVal );
602 }
603 }
604
605 if ( !std::isnan( maxVal ) )
606 {
607 result = QVariant( maxVal );
608 }
609 return result;
610}
611
612static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
613{
614 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
615 double minVal = std::numeric_limits<double>::quiet_NaN();
616 for ( const QVariant &val : values )
617 {
618 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
619 if ( std::isnan( minVal ) )
620 {
621 minVal = testVal;
622 }
623 else if ( !std::isnan( testVal ) )
624 {
625 minVal = std::min( minVal, testVal );
626 }
627 }
628
629 if ( !std::isnan( minVal ) )
630 {
631 result = QVariant( minVal );
632 }
633 return result;
634}
635
636static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
637{
638 //lazy eval, so we need to evaluate nodes now
639
640 //first node is layer id or name
641 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
643 QVariant value = node->eval( parent, context );
645
646 // TODO this expression function is NOT thread safe
648 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
650 if ( !vl )
651 {
652 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
653 return QVariant();
654 }
655
656 // second node is aggregate type
657 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
659 value = node->eval( parent, context );
661 bool ok = false;
662 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
663 if ( !ok )
664 {
665 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
666 return QVariant();
667 }
668
669 // third node is subexpression (or field name)
670 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
672 QString subExpression = node->dump();
673
675 //optional forth node is filter
676 if ( values.count() > 3 )
677 {
678 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
680 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
681 if ( !nl || nl->value().isValid() )
682 parameters.filter = node->dump();
683 }
684
685 //optional fifth node is concatenator
686 if ( values.count() > 4 )
687 {
688 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
690 value = node->eval( parent, context );
692 parameters.delimiter = value.toString();
693 }
694
695 //optional sixth node is order by
696 QString orderBy;
697 if ( values.count() > 5 )
698 {
699 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
701 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
702 if ( !nl || nl->value().isValid() )
703 {
704 orderBy = node->dump();
705 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
706 }
707 }
708
709 QString aggregateError;
710 QVariant result;
711 if ( context )
712 {
713 QString cacheKey;
714 QgsExpression subExp( subExpression );
715 QgsExpression filterExp( parameters.filter );
716
717 const QSet< QString > filterVars = filterExp.referencedVariables();
718 const QSet< QString > subExpVars = subExp.referencedVariables();
719 QSet<QString> allVars = filterVars + subExpVars;
720
721 bool isStatic = true;
722 if ( filterVars.contains( QStringLiteral( "parent" ) )
723 || filterVars.contains( QString() )
724 || subExpVars.contains( QStringLiteral( "parent" ) )
725 || subExpVars.contains( QString() ) )
726 {
727 isStatic = false;
728 }
729 else
730 {
731 for ( const QString &varName : allVars )
732 {
733 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
734 if ( scope && !scope->isStatic( varName ) )
735 {
736 isStatic = false;
737 break;
738 }
739 }
740 }
741
742 if ( isStatic && ! parameters.orderBy.isEmpty() )
743 {
744 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
745 {
746 const QgsExpression &orderByExpression { orderByClause.expression() };
747 if ( orderByExpression.referencedVariables().contains( QStringLiteral( "parent" ) ) || orderByExpression.referencedVariables().contains( QString() ) )
748 {
749 isStatic = false;
750 break;
751 }
752 }
753 }
754
755 if ( !isStatic )
756 {
757 bool ok = false;
758 const QString contextHash = context->uniqueHash( ok, allVars );
759 if ( ok )
760 {
761 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
762 orderBy, contextHash );
763 }
764 }
765 else
766 {
767 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
768 }
769
770 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
771 {
772 return context->cachedValue( cacheKey );
773 }
774
775 QgsExpressionContext subContext( *context );
777 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
778 subContext.appendScope( subScope );
779 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
780
781 if ( ok && !cacheKey.isEmpty() )
782 {
783 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
784 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
785 // associated with it's calculation!
786 context->setCachedValue( cacheKey, result );
787 }
788 }
789 else
790 {
791 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
792 }
793 if ( !ok )
794 {
795 if ( !aggregateError.isEmpty() )
796 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
797 else
798 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
799 return QVariant();
800 }
801
802 return result;
803}
804
805static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
806{
807 if ( !context )
808 {
809 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
810 return QVariant();
811 }
812
813 // first step - find current layer
814
815 // TODO this expression function is NOT thread safe
817 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
819 if ( !vl )
820 {
821 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
822 return QVariant();
823 }
824
825 //lazy eval, so we need to evaluate nodes now
826
827 //first node is relation name
828 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
830 QVariant value = node->eval( parent, context );
832 QString relationId = value.toString();
833 // check relation exists
834 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
835 if ( !relation.isValid() || relation.referencedLayer() != vl )
836 {
837 // check for relations by name
838 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
839 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
840 {
841 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
842 return QVariant();
843 }
844 else
845 {
846 relation = relations.at( 0 );
847 }
848 }
849
850 QgsVectorLayer *childLayer = relation.referencingLayer();
851
852 // second node is aggregate type
853 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
855 value = node->eval( parent, context );
857 bool ok = false;
858 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
859 if ( !ok )
860 {
861 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
862 return QVariant();
863 }
864
865 //third node is subexpression (or field name)
866 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
868 QString subExpression = node->dump();
869
870 //optional fourth node is concatenator
872 if ( values.count() > 3 )
873 {
874 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
876 value = node->eval( parent, context );
878 parameters.delimiter = value.toString();
879 }
880
881 //optional fifth node is order by
882 QString orderBy;
883 if ( values.count() > 4 )
884 {
885 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
887 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
888 if ( !nl || nl->value().isValid() )
889 {
890 orderBy = node->dump();
891 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
892 }
893 }
894
895 if ( !context->hasFeature() )
896 return QVariant();
897 QgsFeature f = context->feature();
898
899 parameters.filter = relation.getRelatedFeaturesFilter( f );
900
901 const QString cacheKey = QStringLiteral( "relagg:%1%:%2:%3:%4:%5:%6" ).arg( relationId, vl->id(),
902 QString::number( static_cast< int >( aggregate ) ),
903 subExpression,
904 parameters.filter,
905 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( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
933{
934 if ( !context )
935 {
936 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
937 return QVariant();
938 }
939
940 // first step - find current layer
941
942 // TODO this expression function is NOT thread safe
944 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
946 if ( !vl )
947 {
948 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
949 return QVariant();
950 }
951
952 //lazy eval, so we need to evaluate nodes now
953
954 //first node is subexpression (or field name)
955 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
957 QString subExpression = node->dump();
958
959 //optional second node is group by
960 QString groupBy;
961 if ( values.count() > 1 )
962 {
963 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
965 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
966 if ( !nl || nl->value().isValid() )
967 groupBy = node->dump();
968 }
969
970 //optional third node is filter
971 if ( values.count() > 2 )
972 {
973 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
975 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
976 if ( !nl || nl->value().isValid() )
977 parameters.filter = node->dump();
978 }
979
980 //optional order by node, if supported
981 QString orderBy;
982 if ( orderByPos >= 0 && values.count() > orderByPos )
983 {
984 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
986 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
987 if ( !nl || nl->value().isValid() )
988 {
989 orderBy = node->dump();
990 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
991 }
992 }
993
994 // build up filter with group by
995
996 // find current group by value
997 if ( !groupBy.isEmpty() )
998 {
999 QgsExpression groupByExp( groupBy );
1000 QVariant groupByValue = groupByExp.evaluate( context );
1001 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
1002 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
1003 QgsExpression::quotedValue( groupByValue ) );
1004 if ( !parameters.filter.isEmpty() )
1005 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).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 = QStringLiteral( "agg:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1033 orderBy, contextHash );
1034 }
1035 }
1036 else
1037 {
1038 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1039 }
1040
1041 if ( context->hasCachedValue( cacheKey ) )
1042 return context->cachedValue( cacheKey );
1043
1044 QVariant result;
1045 bool ok = false;
1046
1047 QgsExpressionContext subContext( *context );
1049 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
1050 subContext.appendScope( subScope );
1051 QString error;
1052 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1053
1054 if ( !ok )
1055 {
1056 if ( !error.isEmpty() )
1057 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1058 else
1059 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1060 return QVariant();
1061 }
1062
1063 // cache value
1064 context->setCachedValue( cacheKey, result );
1065 return result;
1066}
1067
1068
1069static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1070{
1071 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1072}
1073
1074static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1075{
1076 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1077}
1078
1079static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1080{
1081 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1082}
1083
1084static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1085{
1086 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1087}
1088
1089static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1090{
1091 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1092}
1093
1094static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1095{
1096 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1097}
1098
1099static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1100{
1101 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1102}
1103
1104static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1105{
1106 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1107}
1108
1109static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1110{
1111 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1112}
1113
1114static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1115{
1116 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1117}
1118
1119static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1120{
1121 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1122}
1123
1124static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1125{
1126 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1127}
1128
1129static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1130{
1131 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1132}
1133
1134static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1135{
1136 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1137}
1138
1139static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1140{
1141 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1142}
1143
1144static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1145{
1146 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1147}
1148
1149static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1150{
1151 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1152}
1153
1154static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1155{
1156 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1157}
1158
1159static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1160{
1162
1163 //fourth node is concatenator
1164 if ( values.count() > 3 )
1165 {
1166 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1168 QVariant value = node->eval( parent, context );
1170 parameters.delimiter = value.toString();
1171 }
1172
1173 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1174}
1175
1176static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1177{
1179
1180 //fourth node is concatenator
1181 if ( values.count() > 3 )
1182 {
1183 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1185 QVariant value = node->eval( parent, context );
1187 parameters.delimiter = value.toString();
1188 }
1189
1190 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1191}
1192
1193static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1194{
1195 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1196}
1197
1198static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1199{
1200 if ( !context )
1201 return QVariant();
1202
1203 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1204 bool ok = false;
1205 if ( QgsVariantUtils::isNull( scale ) )
1206 return QVariant();
1207
1208 const double v = scale.toDouble( &ok );
1209 if ( ok )
1210 return v;
1211 return QVariant();
1212}
1213
1214static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1215{
1216 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1217 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1218 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1219
1220 // force testValue to sit inside the range specified by the min and max value
1221 if ( testValue <= minValue )
1222 {
1223 return QVariant( minValue );
1224 }
1225 else if ( testValue >= maxValue )
1226 {
1227 return QVariant( maxValue );
1228 }
1229 else
1230 {
1231 return QVariant( testValue );
1232 }
1233}
1234
1235static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1236{
1237 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1238 return QVariant( std::floor( x ) );
1239}
1240
1241static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1244 return QVariant( std::ceil( x ) );
1245}
1246
1247static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1248{
1249 const QVariant value = values.at( 0 );
1250 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1251 {
1252 return QVariant( false );
1253 }
1254 else if ( value.userType() == QMetaType::QString )
1255 {
1256 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1257 return QVariant( !value.toString().isEmpty() );
1258 }
1259 else if ( QgsExpressionUtils::isList( value ) )
1260 {
1261 return !value.toList().isEmpty();
1262 }
1263 return QVariant( value.toBool() );
1264}
1265static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1266{
1267 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1268}
1269static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1270{
1271 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1272}
1273static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1274{
1275 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1276}
1277
1278static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1279{
1280 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1281 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1282 if ( format.isEmpty() && !language.isEmpty() )
1283 {
1284 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1285 return QVariant( QDateTime() );
1286 }
1287
1288 if ( format.isEmpty() && language.isEmpty() )
1289 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1290
1291 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1292 QLocale locale = QLocale();
1293 if ( !language.isEmpty() )
1294 {
1295 locale = QLocale( language );
1296 }
1297
1298 QDateTime datetime = locale.toDateTime( datetimestring, format );
1299 if ( !datetime.isValid() )
1300 {
1301 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1302 datetime = QDateTime();
1303 }
1304 return QVariant( datetime );
1305}
1306
1307static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1308{
1309 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1310 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1311 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1312
1313 const QDate date( year, month, day );
1314 if ( !date.isValid() )
1315 {
1316 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1317 return QVariant();
1318 }
1319 return QVariant( date );
1320}
1321
1322static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1323{
1324 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1325 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1326 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1327
1328 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1329 if ( !time.isValid() )
1330 {
1331 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1332 return QVariant();
1333 }
1334 return QVariant( time );
1335}
1336
1337static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1338{
1339 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1340 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1341 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1342 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1343 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1344 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1345
1346 const QDate date( year, month, day );
1347 if ( !date.isValid() )
1348 {
1349 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1350 return QVariant();
1351 }
1352 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1353 if ( !time.isValid() )
1354 {
1355 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1356 return QVariant();
1357 }
1358 return QVariant( QDateTime( date, time ) );
1359}
1360
1361static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1362{
1363 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1364 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1365 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1366 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1367 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1368 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1369 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1370
1371 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1372}
1373
1374static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1375{
1376 for ( const QVariant &value : values )
1377 {
1378 if ( QgsVariantUtils::isNull( value ) )
1379 continue;
1380 return value;
1381 }
1382 return QVariant();
1383}
1384
1385static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1386{
1387 const QVariant val1 = values.at( 0 );
1388 const QVariant val2 = values.at( 1 );
1389
1390 if ( val1 == val2 )
1391 return QVariant();
1392 else
1393 return val1;
1394}
1395
1396static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1397{
1398 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1399 return QVariant( str.toLower() );
1400}
1401static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1402{
1403 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1404 return QVariant( str.toUpper() );
1405}
1406static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1407{
1408 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409 QStringList elems = str.split( ' ' );
1410 for ( int i = 0; i < elems.size(); i++ )
1411 {
1412 if ( elems[i].size() > 1 )
1413 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1414 }
1415 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1416}
1417
1418static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1419{
1420 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1421 return QVariant( str.trimmed() );
1422}
1423
1424static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1425{
1426 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1427
1428 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1429
1430 const QRegularExpression re( QStringLiteral( "^([%1]*)" ).arg( QRegularExpression::escape( characters ) ) );
1431 str.replace( re, QString() );
1432 return QVariant( str );
1433}
1434
1435static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1436{
1437 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1438
1439 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1440
1441 const QRegularExpression re( QStringLiteral( "([%1]*)$" ).arg( QRegularExpression::escape( characters ) ) );
1442 str.replace( re, QString() );
1443 return QVariant( str );
1444}
1445
1446static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1447{
1448 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1449 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1450 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1451}
1452
1453static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1454{
1455 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1456 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1457 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1458}
1459
1460static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1461{
1462 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1463 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1464 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1465 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1466}
1467
1468static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1469{
1470 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1471 return QVariant( QgsStringUtils::soundex( string ) );
1472}
1473
1474static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1475{
1476 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1477 return QVariant( QString( character ) );
1478}
1479
1480static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1481{
1482 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1483
1484 if ( value.isEmpty() )
1485 {
1486 return QVariant();
1487 }
1488
1489 int res = value.at( 0 ).unicode();
1490 return QVariant( res );
1491}
1492
1493static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1494{
1495 if ( values.length() == 2 || values.length() == 3 )
1496 {
1497 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1498 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1499
1500 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1501
1502 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1503 }
1504
1505 return QVariant();
1506}
1507
1508static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1509{
1510 // two variants, one for geometry, one for string
1511
1512 //geometry variant
1513 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1514 if ( !geom.isNull() )
1515 {
1516 if ( geom.type() == Qgis::GeometryType::Line )
1517 return QVariant( geom.length() );
1518 else
1519 return QVariant();
1520 }
1521
1522 //otherwise fall back to string variant
1523 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1524 return QVariant( str.length() );
1525}
1526
1527static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1528{
1529 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1530
1531 if ( geom.type() != Qgis::GeometryType::Line )
1532 return QVariant();
1533
1534 double totalLength = 0;
1535 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1536 {
1537 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1538 {
1539 totalLength += line->length3D();
1540 }
1541 else
1542 {
1543 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1544 totalLength += segmentized->length3D();
1545 }
1546 }
1547
1548 return totalLength;
1549}
1550
1551
1552static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1553{
1554 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1555 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1556 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1557}
1558
1559static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1560{
1561 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1562 {
1563 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1564 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1565 QVector< QPair< QString, QString > > mapItems;
1566
1567 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1568 {
1569 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1570 }
1571
1572 // larger keys should be replaced first since they may contain whole smaller keys
1573 std::sort( mapItems.begin(),
1574 mapItems.end(),
1575 []( const QPair< QString, QString > &pair1,
1576 const QPair< QString, QString > &pair2 )
1577 {
1578 return ( pair1.first.length() > pair2.first.length() );
1579 } );
1580
1581 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1582 {
1583 str = str.replace( it->first, it->second );
1584 }
1585
1586 return QVariant( str );
1587 }
1588 else if ( values.count() == 3 )
1589 {
1590 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1591 QVariantList before;
1592 QVariantList after;
1593 bool isSingleReplacement = false;
1594
1595 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1596 {
1597 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1598 }
1599 else
1600 {
1601 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1602 }
1603
1604 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1605 {
1606 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1607 isSingleReplacement = true;
1608 }
1609 else
1610 {
1611 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1612 }
1613
1614 if ( !isSingleReplacement && before.length() != after.length() )
1615 {
1616 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1617 return QVariant();
1618 }
1619
1620 for ( int i = 0; i < before.length(); i++ )
1621 {
1622 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1623 }
1624
1625 return QVariant( str );
1626 }
1627 else
1628 {
1629 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1630 return QVariant();
1631 }
1632}
1633
1634static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1635{
1636 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1637 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1638 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1639
1640 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1641 if ( !re.isValid() )
1642 {
1643 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1644 return QVariant();
1645 }
1646 return QVariant( str.replace( re, after ) );
1647}
1648
1649static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1650{
1651 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1652 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1653
1654 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1655 if ( !re.isValid() )
1656 {
1657 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1658 return QVariant();
1659 }
1660 return QVariant( ( str.indexOf( re ) + 1 ) );
1661}
1662
1663static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1664{
1665 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1666 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1667 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1668
1669 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1670 if ( !re.isValid() )
1671 {
1672 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1673 return QVariant();
1674 }
1675
1676 QRegularExpressionMatch matches = re.match( str );
1677 if ( matches.hasMatch() )
1678 {
1679 QVariantList array;
1680 QStringList list = matches.capturedTexts();
1681
1682 // Skip the first string to only return captured groups
1683 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1684 {
1685 array += ( !( *it ).isEmpty() ) ? *it : empty;
1686 }
1687
1688 return QVariant( array );
1689 }
1690 else
1691 {
1692 return QVariant();
1693 }
1694}
1695
1696static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1697{
1698 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1699 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1700
1701 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1702 if ( !re.isValid() )
1703 {
1704 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1705 return QVariant();
1706 }
1707
1708 // extract substring
1709 QRegularExpressionMatch match = re.match( str );
1710 if ( match.hasMatch() )
1711 {
1712 // return first capture
1713 if ( match.lastCapturedIndex() > 0 )
1714 {
1715 // a capture group was present, so use that
1716 return QVariant( match.captured( 1 ) );
1717 }
1718 else
1719 {
1720 // no capture group, so using all match
1721 return QVariant( match.captured( 0 ) );
1722 }
1723 }
1724 else
1725 {
1726 return QVariant( "" );
1727 }
1728}
1729
1730static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1731{
1732 QString uuid = QUuid::createUuid().toString();
1733 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1734 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1735 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1736 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1737 return uuid;
1738}
1739
1740static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1741{
1742 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1743 return QVariant();
1744
1745 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1746 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1747
1748 int len = 0;
1749 if ( values.at( 2 ).isValid() )
1750 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1751 else
1752 len = str.size();
1753
1754 if ( from < 0 )
1755 {
1756 from = str.size() + from;
1757 if ( from < 0 )
1758 {
1759 from = 0;
1760 }
1761 }
1762 else if ( from > 0 )
1763 {
1764 //account for the fact that substr() starts at 1
1765 from -= 1;
1766 }
1767
1768 if ( len < 0 )
1769 {
1770 len = str.size() + len - from;
1771 if ( len < 0 )
1772 {
1773 len = 0;
1774 }
1775 }
1776
1777 return QVariant( str.mid( from, len ) );
1778}
1779static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1780{
1781 FEAT_FROM_CONTEXT( context, f )
1782 return QVariant( f.id() );
1783}
1784
1785static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1786{
1787 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1788 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1789 bool foundLayer = false;
1790 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1791 {
1792 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1793 if ( !layer || !layer->dataProvider() )
1794 {
1795 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1796 return QVariant();
1797 }
1798
1799 if ( bandNb < 1 || bandNb > layer->bandCount() )
1800 {
1801 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1802 return QVariant();
1803 }
1804
1805 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1806 {
1807 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1808 return QVariant();
1809 }
1810
1811 QgsPointXY point = geom.asPoint();
1812 if ( geom.isMultipart() )
1813 {
1814 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1815 if ( multiPoint.count() == 1 )
1816 {
1817 point = multiPoint[0];
1818 }
1819 else
1820 {
1821 // if the geometry contains more than one part, return an undefined value
1822 return QVariant();
1823 }
1824 }
1825
1826 double value = layer->dataProvider()->sample( point, bandNb );
1827 return std::isnan( value ) ? QVariant() : value;
1828 },
1829 foundLayer );
1830
1831 if ( !foundLayer )
1832 {
1833 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1834 return QVariant();
1835 }
1836 else
1837 {
1838 return res;
1839 }
1840}
1841
1842static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1843{
1844 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1845 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1846
1847 bool foundLayer = false;
1848 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1849 {
1850 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1851 if ( !layer || !layer->dataProvider() )
1852 {
1853 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1854 return QVariant();
1855 }
1856
1857 if ( bandNb < 1 || bandNb > layer->bandCount() )
1858 {
1859 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1860 return QVariant();
1861 }
1862
1863 if ( std::isnan( value ) )
1864 {
1865 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1866 return QVariant();
1867 }
1868
1869 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1870 {
1871 return QVariant();
1872 }
1873
1874 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1875 if ( data.isEmpty() )
1876 {
1877 return QVariant();
1878 }
1879
1880 QVariantMap result;
1881 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1882 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1883 {
1884 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1885 if ( field.isColor() || field.isRamp() )
1886 {
1887 continue;
1888 }
1889 result.insert( fields.at( idx ).name, data.at( idx ) );
1890 }
1891
1892 return result;
1893 }, foundLayer );
1894
1895 if ( !foundLayer )
1896 {
1897 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1898 return QVariant();
1899 }
1900 else
1901 {
1902 return res;
1903 }
1904}
1905
1906static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1907{
1908 if ( !context )
1909 return QVariant();
1910
1911 return context->feature();
1912}
1913
1914static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1915{
1916 QgsFeature feature;
1917 QString attr;
1918 if ( values.size() == 1 )
1919 {
1920 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1921 feature = context->feature();
1922 }
1923 else if ( values.size() == 2 )
1924 {
1925 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1926 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1927 }
1928 else
1929 {
1930 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1931 return QVariant();
1932 }
1933
1934 return feature.attribute( attr );
1935}
1936
1937static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1938{
1939 QString table { R"html(
1940 <table>
1941 <thead>
1942 <tr><th>%1</th></tr>
1943 </thead>
1944 <tbody>
1945 <tr><td>%2</td></tr>
1946 </tbody>
1947 </table>)html" };
1948 QVariantMap dict;
1949 if ( values.size() == 1 )
1950 {
1951 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1952 }
1953 else
1954 {
1955 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1956 return QVariant();
1957 }
1958
1959 if ( dict.isEmpty() )
1960 {
1961 return QVariant();
1962 }
1963
1964 QStringList headers;
1965 QStringList cells;
1966
1967 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1968 {
1969 headers.push_back( it.key().toHtmlEscaped() );
1970 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1971 }
1972
1973 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1974}
1975
1976static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1977{
1978 QString table { R"html(
1979 <dl>
1980 %1
1981 </dl>)html" };
1982 QVariantMap dict;
1983 if ( values.size() == 1 )
1984 {
1985 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1986 }
1987 else
1988 {
1989 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1990 return QVariant();
1991 }
1992
1993 if ( dict.isEmpty() )
1994 {
1995 return QVariant();
1996 }
1997
1998 QString rows;
1999
2000 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2001 {
2002 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2003 }
2004
2005 return table.arg( rows );
2006}
2007
2008static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2009{
2010 QVariant layer;
2011 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2012 {
2013 layer = context->variable( QStringLiteral( "layer" ) );
2014 }
2015 else
2016 {
2017 //first node is layer id or name
2018 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2020 layer = node->eval( parent, context );
2022 }
2023
2024 QgsFeature feature;
2025 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2026 {
2027 feature = context->feature();
2028 }
2029 else
2030 {
2031 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2032 }
2033
2035 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2036 if ( strength == QLatin1String( "hard" ) )
2037 {
2039 }
2040 else if ( strength == QLatin1String( "soft" ) )
2041 {
2043 }
2044
2045 bool foundLayer = false;
2046 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2047 {
2048 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2049 if ( !layer )
2050 {
2051 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2052 return QVariant();
2053 }
2054
2055 const QgsFields fields = layer->fields();
2056 bool valid = true;
2057 for ( int i = 0; i < fields.size(); i++ )
2058 {
2059 QStringList errors;
2060 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2061 if ( !valid )
2062 {
2063 break;
2064 }
2065 }
2066
2067 return valid;
2068 }, foundLayer );
2069
2070 if ( !foundLayer )
2071 {
2072 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2073 return QVariant();
2074 }
2075
2076 return res;
2077}
2078
2079static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2080{
2081 QVariant layer;
2082 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2083 {
2084 layer = context->variable( QStringLiteral( "layer" ) );
2085 }
2086 else
2087 {
2088 //first node is layer id or name
2089 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2091 layer = node->eval( parent, context );
2093 }
2094
2095 QgsFeature feature;
2096 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2097 {
2098 feature = context->feature();
2099 }
2100 else
2101 {
2102 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2103 }
2104
2106 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2107 if ( strength == QLatin1String( "hard" ) )
2108 {
2110 }
2111 else if ( strength == QLatin1String( "soft" ) )
2112 {
2114 }
2115
2116 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2117
2118 bool foundLayer = false;
2119 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2120 {
2121 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2122 if ( !layer )
2123 {
2124 return QVariant();
2125 }
2126
2127 const int fieldIndex = layer->fields().indexFromName( attributeName );
2128 if ( fieldIndex == -1 )
2129 {
2130 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2131 return QVariant();
2132 }
2133
2134 QStringList errors;
2135 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2136 return valid;
2137 }, foundLayer );
2138
2139 if ( !foundLayer )
2140 {
2141 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2142 return QVariant();
2143 }
2144
2145 return res;
2146}
2147
2148static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2149{
2150 QgsFeature feature;
2151 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2152 {
2153 feature = context->feature();
2154 }
2155 else
2156 {
2157 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2158 }
2159
2160 const QgsFields fields = feature.fields();
2161 QVariantMap result;
2162 for ( int i = 0; i < fields.count(); ++i )
2163 {
2164 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2165 }
2166 return result;
2167}
2168
2169static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2170{
2171 QgsVectorLayer *layer = nullptr;
2172 QgsFeature feature;
2173
2174 // TODO this expression function is NOT thread safe
2176 if ( values.isEmpty() )
2177 {
2178 feature = context->feature();
2179 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2180 }
2181 else if ( values.size() == 1 )
2182 {
2183 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2184 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2185 }
2186 else if ( values.size() == 2 )
2187 {
2188 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2189 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2190 }
2191 else
2192 {
2193 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2194 return QVariant();
2195 }
2197
2198 if ( !layer )
2199 {
2200 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2201 return QVariant();
2202 }
2203
2204 if ( !feature.isValid() )
2205 {
2206 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2207 return QVariant();
2208 }
2209
2210 const QgsFields fields = feature.fields();
2211 QVariantMap result;
2212 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2213 {
2214 const QString fieldName { fields.at( fieldIndex ).name() };
2215 const QVariant attributeVal = feature.attribute( fieldIndex );
2216 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2217 if ( context && context->hasCachedValue( cacheValueKey ) )
2218 {
2219 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2220 }
2221 else
2222 {
2223 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2225 QVariant cache;
2226 if ( context )
2227 {
2228 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2229
2230 if ( !context->hasCachedValue( cacheKey ) )
2231 {
2232 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2233 context->setCachedValue( cacheKey, cache );
2234 }
2235 else
2236 {
2237 cache = context->cachedValue( cacheKey );
2238 }
2239 }
2240 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2241
2242 result.insert( fields.at( fieldIndex ).name(), value );
2243
2244 if ( context )
2245 {
2246 context->setCachedValue( cacheValueKey, value );
2247 }
2248
2249 }
2250 }
2251 return result;
2252}
2253
2254static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2255{
2256 QgsVectorLayer *layer = nullptr;
2257 QgsFeature feature;
2258 bool evaluate = true;
2259
2260 // TODO this expression function is NOT thread safe
2262 if ( values.isEmpty() )
2263 {
2264 feature = context->feature();
2265 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2266 }
2267 else if ( values.size() == 1 )
2268 {
2269 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2270 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2271 }
2272 else if ( values.size() == 2 )
2273 {
2274 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2275 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2276 }
2277 else if ( values.size() == 3 )
2278 {
2279 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2280 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2281 evaluate = values.value( 2 ).toBool();
2282 }
2283 else
2284 {
2285 if ( isMaptip )
2286 {
2287 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2288 }
2289 else
2290 {
2291 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2292 }
2293 return QVariant();
2294 }
2295
2296 if ( !layer )
2297 {
2298 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2299 return QVariant( );
2300 }
2302
2303 if ( !feature.isValid() )
2304 {
2305 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2306 return QVariant( );
2307 }
2308
2309 if ( ! evaluate )
2310 {
2311 if ( isMaptip )
2312 {
2313 return layer->mapTipTemplate();
2314 }
2315 else
2316 {
2317 return layer->displayExpression();
2318 }
2319 }
2320
2321 QgsExpressionContext subContext( *context );
2322 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2323 subContext.setFeature( feature );
2324
2325 if ( isMaptip )
2326 {
2327 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2328 }
2329 else
2330 {
2331 QgsExpression exp( layer->displayExpression() );
2332 exp.prepare( &subContext );
2333 return exp.evaluate( &subContext ).toString();
2334 }
2335}
2336
2337static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2338{
2339 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2340}
2341
2342static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2343{
2344 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2345}
2346
2347static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2348{
2349 QgsFeature feature;
2350 QVariant layer;
2351 if ( values.isEmpty() )
2352 {
2353 feature = context->feature();
2354 layer = context->variable( QStringLiteral( "layer" ) );
2355 }
2356 else if ( values.size() == 1 )
2357 {
2358 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2359 layer = context->variable( QStringLiteral( "layer" ) );
2360 }
2361 else if ( values.size() == 2 )
2362 {
2363 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2364 layer = values.at( 0 );
2365 }
2366 else
2367 {
2368 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2369 return QVariant();
2370 }
2371
2372 bool foundLayer = false;
2373 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2374 {
2375 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2376 if ( !layer || !feature.isValid() )
2377 {
2378 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2379 }
2380
2381 return layer->selectedFeatureIds().contains( feature.id() );
2382 }, foundLayer );
2383 if ( !foundLayer )
2384 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2385 else
2386 return res;
2387}
2388
2389static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2390{
2391 QVariant layer;
2392
2393 if ( values.isEmpty() )
2394 layer = context->variable( QStringLiteral( "layer" ) );
2395 else if ( values.count() == 1 )
2396 layer = values.at( 0 );
2397 else
2398 {
2399 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2400 return QVariant();
2401 }
2402
2403 bool foundLayer = false;
2404 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2405 {
2406 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2407 if ( !layer )
2408 {
2409 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2410 }
2411
2412 return layer->selectedFeatureCount();
2413 }, foundLayer );
2414 if ( !foundLayer )
2415 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2416 else
2417 return res;
2418}
2419
2420static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2421{
2422 static QMap<QString, qlonglong> counterCache;
2423 QVariant functionResult;
2424
2425 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2426 {
2427 QString database;
2428
2429 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2430
2431 if ( layer )
2432 {
2433 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2434 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2435 if ( database.isEmpty() )
2436 {
2437 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2438 }
2439 }
2440 else
2441 {
2442 database = databaseArgument;
2443 }
2444
2445 const QString table = values.at( 1 ).toString();
2446 const QString idColumn = values.at( 2 ).toString();
2447 const QString filterAttribute = values.at( 3 ).toString();
2448 const QVariant filterValue = values.at( 4 ).toString();
2449 const QVariantMap defaultValues = values.at( 5 ).toMap();
2450
2451 // read from database
2453 sqlite3_statement_unique_ptr sqliteStatement;
2454
2455 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2456 {
2457 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2458 functionResult = QVariant();
2459 return;
2460 }
2461
2462 QString errorMessage;
2463 QString currentValSql;
2464
2465 qlonglong nextId = 0;
2466 bool cachedMode = false;
2467 bool valueRetrieved = false;
2468
2469 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2470
2471 // Running in transaction mode, check for cached value first
2472 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2473 {
2474 cachedMode = true;
2475
2476 auto cachedCounter = counterCache.find( cacheString );
2477
2478 if ( cachedCounter != counterCache.end() )
2479 {
2480 qlonglong &cachedValue = cachedCounter.value();
2481 nextId = cachedValue;
2482 nextId += 1;
2483 cachedValue = nextId;
2484 valueRetrieved = true;
2485 }
2486 }
2487
2488 // Either not in cached mode or no cached value found, obtain from DB
2489 if ( !cachedMode || !valueRetrieved )
2490 {
2491 int result = SQLITE_ERROR;
2492
2493 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2494 if ( !filterAttribute.isNull() )
2495 {
2496 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2497 }
2498
2499 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2500
2501 if ( result == SQLITE_OK )
2502 {
2503 nextId = 0;
2504 if ( sqliteStatement.step() == SQLITE_ROW )
2505 {
2506 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2507 }
2508
2509 // If in cached mode: add value to cache and connect to transaction
2510 if ( cachedMode && result == SQLITE_OK )
2511 {
2512 counterCache.insert( cacheString, nextId );
2513
2514 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2515 {
2516 counterCache.remove( cacheString );
2517 } );
2518 }
2519 valueRetrieved = true;
2520 }
2521 }
2522
2523 if ( valueRetrieved )
2524 {
2525 QString upsertSql;
2526 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2527 QStringList cols;
2528 QStringList vals;
2529 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2530 vals << QgsSqliteUtils::quotedValue( nextId );
2531
2532 if ( !filterAttribute.isNull() )
2533 {
2534 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2535 vals << QgsSqliteUtils::quotedValue( filterValue );
2536 }
2537
2538 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2539 {
2540 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2541 vals << iter.value().toString();
2542 }
2543
2544 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2545 upsertSql += QLatin1String( " VALUES " );
2546 upsertSql += '(' + vals.join( ',' ) + ')';
2547
2548 int result = SQLITE_ERROR;
2549 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2550 {
2551 QgsTransaction *transaction = layer->dataProvider()->transaction();
2552 if ( transaction->executeSql( upsertSql, errorMessage ) )
2553 {
2554 result = SQLITE_OK;
2555 }
2556 }
2557 else
2558 {
2559 result = sqliteDb.exec( upsertSql, errorMessage );
2560 }
2561 if ( result == SQLITE_OK )
2562 {
2563 functionResult = QVariant( nextId );
2564 return;
2565 }
2566 else
2567 {
2568 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2569 functionResult = QVariant();
2570 return;
2571 }
2572 }
2573
2574 functionResult = QVariant();
2575 };
2576
2577 bool foundLayer = false;
2578 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2579 {
2580 fetchAndIncrementFunc( layer, QString() );
2581 }, foundLayer );
2582 if ( !foundLayer )
2583 {
2584 const QString databasePath = values.at( 0 ).toString();
2585 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2586 {
2587 fetchAndIncrementFunc( nullptr, databasePath );
2588 } );
2589 }
2590
2591 return functionResult;
2592}
2593
2594static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2595{
2596 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2597 if ( !crs.isValid() )
2598 return QVariant();
2599 return crs.authid();
2600}
2601
2602static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2603{
2604 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2605 QgsCoordinateReferenceSystem crs( definition );
2606
2607 if ( !crs.isValid() )
2608 {
2609 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to cordinate reference system" ).arg( definition ) );
2610 }
2611
2612 return QVariant::fromValue( crs );
2613}
2614
2615static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2616{
2617 QString concat;
2618 for ( const QVariant &value : values )
2619 {
2620 if ( !QgsVariantUtils::isNull( value ) )
2621 concat += QgsExpressionUtils::getStringValue( value, parent );
2622 }
2623 return concat;
2624}
2625
2626static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2627{
2628 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2629 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2630}
2631
2632static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2633{
2634 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2635 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2636 return string.right( pos );
2637}
2638
2639static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2640{
2641 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2642 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2643 return string.left( pos );
2644}
2645
2646static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2647{
2648 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2649 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2650 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2651 return string.leftJustified( length, fill.at( 0 ), true );
2652}
2653
2654static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2655{
2656 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2657 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2658 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2659 return string.rightJustified( length, fill.at( 0 ), true );
2660}
2661
2662static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2663{
2664 if ( values.size() < 1 )
2665 {
2666 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2667 return QVariant();
2668 }
2669
2670 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2671 for ( int n = 1; n < values.length(); n++ )
2672 {
2673 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2674 }
2675 return string;
2676}
2677
2678
2679static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2680{
2681 return QVariant( QDateTime::currentDateTime() );
2682}
2683
2684static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2685{
2686 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2687 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2688 if ( format.isEmpty() && !language.isEmpty() )
2689 {
2690 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2691 return QVariant( QDate() );
2692 }
2693
2694 if ( format.isEmpty() && language.isEmpty() )
2695 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2696
2697 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2698 QLocale locale = QLocale();
2699 if ( !language.isEmpty() )
2700 {
2701 locale = QLocale( language );
2702 }
2703
2704 QDate date = locale.toDate( datestring, format );
2705 if ( !date.isValid() )
2706 {
2707 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2708 date = QDate();
2709 }
2710 return QVariant( date );
2711}
2712
2713static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2714{
2715 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2716 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2717 if ( format.isEmpty() && !language.isEmpty() )
2718 {
2719 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2720 return QVariant( QTime() );
2721 }
2722
2723 if ( format.isEmpty() && language.isEmpty() )
2724 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2725
2726 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2727 QLocale locale = QLocale();
2728 if ( !language.isEmpty() )
2729 {
2730 locale = QLocale( language );
2731 }
2732
2733 QTime time = locale.toTime( timestring, format );
2734 if ( !time.isValid() )
2735 {
2736 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2737 time = QTime();
2738 }
2739 return QVariant( time );
2740}
2741
2742static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2743{
2744 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2745}
2746
2747/*
2748 * DMS functions
2749 */
2750
2751static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2752{
2753 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2754 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2755 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2756
2757 QString formatString;
2758 if ( values.count() > 3 )
2759 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2760
2762 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2763 {
2765 }
2766 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2767 {
2769 }
2770 else if ( ! formatString.isEmpty() )
2771 {
2772 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2773 return QVariant();
2774 }
2775
2776 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2777 {
2778 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2779 }
2780 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2781 {
2782 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2783 }
2784 else
2785 {
2786 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2787 return QVariant();
2788 }
2789}
2790
2791static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2792{
2794 return floatToDegreeFormat( format, values, context, parent, node );
2795}
2796
2797static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2798{
2799 double value = 0.0;
2800 bool ok = false;
2801 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2802
2803 return ok ? QVariant( value ) : QVariant();
2804}
2805
2806static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2807{
2809 return floatToDegreeFormat( format, values, context, parent, node );
2810}
2811
2812static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2813{
2814 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2815 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2816 qint64 seconds = d2.secsTo( d1 );
2817 return QVariant::fromValue( QgsInterval( seconds ) );
2818}
2819
2820static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2821{
2822 if ( !values.at( 0 ).canConvert<QDate>() )
2823 return QVariant();
2824
2825 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2826 if ( !date.isValid() )
2827 return QVariant();
2828
2829 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2830 // (to match PostgreSQL behavior)
2831 return date.dayOfWeek() % 7;
2832}
2833
2834static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2835{
2836 QVariant value = values.at( 0 );
2837 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2838 if ( inter.isValid() )
2839 {
2840 return QVariant( inter.days() );
2841 }
2842 else
2843 {
2844 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2845 return QVariant( d1.date().day() );
2846 }
2847}
2848
2849static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2850{
2851 QVariant value = values.at( 0 );
2852 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2853 if ( inter.isValid() )
2854 {
2855 return QVariant( inter.years() );
2856 }
2857 else
2858 {
2859 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2860 return QVariant( d1.date().year() );
2861 }
2862}
2863
2864static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2865{
2866 QVariant value = values.at( 0 );
2867 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2868 if ( inter.isValid() )
2869 {
2870 return QVariant( inter.months() );
2871 }
2872 else
2873 {
2874 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2875 return QVariant( d1.date().month() );
2876 }
2877}
2878
2879static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2880{
2881 QVariant value = values.at( 0 );
2882 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2883 if ( inter.isValid() )
2884 {
2885 return QVariant( inter.weeks() );
2886 }
2887 else
2888 {
2889 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2890 return QVariant( d1.date().weekNumber() );
2891 }
2892}
2893
2894static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2895{
2896 QVariant value = values.at( 0 );
2897 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2898 if ( inter.isValid() )
2899 {
2900 return QVariant( inter.hours() );
2901 }
2902 else
2903 {
2904 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2905 return QVariant( t1.hour() );
2906 }
2907}
2908
2909static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2910{
2911 QVariant value = values.at( 0 );
2912 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2913 if ( inter.isValid() )
2914 {
2915 return QVariant( inter.minutes() );
2916 }
2917 else
2918 {
2919 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2920 return QVariant( t1.minute() );
2921 }
2922}
2923
2924static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2925{
2926 QVariant value = values.at( 0 );
2927 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2928 if ( inter.isValid() )
2929 {
2930 return QVariant( inter.seconds() );
2931 }
2932 else
2933 {
2934 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2935 return QVariant( t1.second() );
2936 }
2937}
2938
2939static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2940{
2941 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2942 if ( dt.isValid() )
2943 {
2944 return QVariant( dt.toMSecsSinceEpoch() );
2945 }
2946 else
2947 {
2948 return QVariant();
2949 }
2950}
2951
2952static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2953{
2954 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2955 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2956 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2957}
2958
2959static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2960{
2961 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2962 if ( parent->hasEvalError() )
2963 {
2964 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2965 return QVariant();
2966 }
2967 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2968 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2969}
2970
2971static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2973 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2974 if ( parent->hasEvalError() )
2975 {
2976 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2977 return QVariant();
2978 }
2979 bool ok;
2980 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2981}
2982
2983#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2984 if ( !(f).hasGeometry() ) \
2985 return QVariant(); \
2986 QgsGeometry g = (f).geometry(); \
2987 if ( (g).type() != (geomtype) ) \
2988 return QVariant();
2989
2990static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2991{
2992 FEAT_FROM_CONTEXT( context, f )
2994 if ( g.isMultipart() )
2995 {
2996 return g.asMultiPoint().at( 0 ).x();
2997 }
2998 else
2999 {
3000 return g.asPoint().x();
3001 }
3002}
3003
3004static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3005{
3006 FEAT_FROM_CONTEXT( context, f )
3008 if ( g.isMultipart() )
3009 {
3010 return g.asMultiPoint().at( 0 ).y();
3011 }
3012 else
3013 {
3014 return g.asPoint().y();
3015 }
3016}
3017
3018static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3019{
3020 FEAT_FROM_CONTEXT( context, f )
3022
3023 if ( g.isEmpty() )
3024 return QVariant();
3025
3026 const QgsAbstractGeometry *abGeom = g.constGet();
3027
3028 if ( g.isEmpty() || !abGeom->is3D() )
3029 return QVariant();
3030
3031 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3032 {
3033 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3034 if ( point )
3035 return point->z();
3036 }
3037 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3038 {
3039 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3040 {
3041 if ( collection->numGeometries() > 0 )
3042 {
3043 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3044 return point->z();
3045 }
3046 }
3047 }
3048
3049 return QVariant();
3050}
3051
3052static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3053{
3054 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3055 if ( geom.isNull() )
3056 return QVariant();
3057
3058 bool isValid = geom.isGeosValid();
3059
3060 return QVariant( isValid );
3061}
3062
3063static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3064{
3065 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3066 if ( geom.isNull() )
3067 return QVariant();
3068
3069 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3070#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3072#else
3074#endif
3075 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3077 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3079
3080 const bool keepCollapsed = values.value( 2 ).toBool();
3081
3082 QgsGeometry valid;
3083 try
3084 {
3085 valid = geom.makeValid( method, keepCollapsed );
3086 }
3087 catch ( QgsNotSupportedException & )
3088 {
3089 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3090 return QVariant();
3091 }
3092
3093 return QVariant::fromValue( valid );
3094}
3095
3096static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3097{
3098 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3099 if ( geom.isNull() )
3100 return QVariant();
3101
3102 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3103 QVariantList array;
3104 for ( int i = 0; i < multiGeom.size(); ++i )
3105 {
3106 array += QVariant::fromValue( multiGeom.at( i ) );
3107 }
3108
3109 return array;
3110}
3111
3112static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3113{
3114 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3115 if ( geom.isNull() )
3116 return QVariant();
3117
3118 //if single point, return the point's x coordinate
3119 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3120 {
3121 return geom.asPoint().x();
3122 }
3123
3124 //otherwise return centroid x
3125 QgsGeometry centroid = geom.centroid();
3126 QVariant result( centroid.asPoint().x() );
3127 return result;
3128}
3129
3130static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3131{
3132 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3133 if ( geom.isNull() )
3134 return QVariant();
3135
3136 //if single point, return the point's y coordinate
3137 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3138 {
3139 return geom.asPoint().y();
3140 }
3141
3142 //otherwise return centroid y
3143 QgsGeometry centroid = geom.centroid();
3144 QVariant result( centroid.asPoint().y() );
3145 return result;
3146}
3147
3148static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3149{
3150 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3151 if ( geom.isNull() )
3152 return QVariant(); //or 0?
3153
3154 if ( !geom.constGet()->is3D() )
3155 return QVariant();
3156
3157 //if single point, return the point's z coordinate
3158 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3159 {
3160 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3161 if ( point )
3162 return point->z();
3163 }
3164 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3165 {
3166 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3167 {
3168 if ( collection->numGeometries() == 1 )
3169 {
3170 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3171 return point->z();
3172 }
3173 }
3174 }
3175
3176 return QVariant();
3177}
3178
3179static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3180{
3181 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3182 if ( geom.isNull() )
3183 return QVariant(); //or 0?
3184
3185 if ( !geom.constGet()->isMeasure() )
3186 return QVariant();
3187
3188 //if single point, return the point's m value
3189 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3190 {
3191 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3192 if ( point )
3193 return point->m();
3194 }
3195 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3196 {
3197 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3198 {
3199 if ( collection->numGeometries() == 1 )
3200 {
3201 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3202 return point->m();
3203 }
3204 }
3205 }
3206
3207 return QVariant();
3208}
3209
3210static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3211{
3212 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3213
3214 if ( geom.isNull() )
3215 return QVariant();
3216
3217 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3218
3219 if ( idx < 0 )
3220 {
3221 //negative idx
3222 int count = geom.constGet()->nCoordinates();
3223 idx = count + idx;
3224 }
3225 else
3226 {
3227 //positive idx is 1 based
3228 idx -= 1;
3229 }
3230
3231 QgsVertexId vId;
3232 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3233 {
3234 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3235 return QVariant();
3236 }
3237
3238 QgsPoint point = geom.constGet()->vertexAt( vId );
3239 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3240}
3241
3242static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3243{
3244 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3245
3246 if ( geom.isNull() )
3247 return QVariant();
3248
3249 QgsVertexId vId;
3250 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3251 {
3252 return QVariant();
3253 }
3254
3255 QgsPoint point = geom.constGet()->vertexAt( vId );
3256 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3257}
3258
3259static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3260{
3261 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3262
3263 if ( geom.isNull() )
3264 return QVariant();
3265
3266 QgsVertexId vId;
3267 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3268 {
3269 return QVariant();
3270 }
3271
3272 QgsPoint point = geom.constGet()->vertexAt( vId );
3273 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3274}
3275
3276static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3277{
3278 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3279
3280 if ( geom.isNull() )
3281 return QVariant();
3282
3283 bool ignoreClosing = false;
3284 if ( values.length() > 1 )
3285 {
3286 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3287 }
3288
3289 QgsMultiPoint *mp = new QgsMultiPoint();
3290
3291 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3292 for ( const QgsRingSequence &part : sequence )
3293 {
3294 for ( const QgsPointSequence &ring : part )
3295 {
3296 bool skipLast = false;
3297 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3298 {
3299 skipLast = true;
3300 }
3301
3302 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3303 {
3304 mp->addGeometry( ring.at( i ).clone() );
3305 }
3306 }
3307 }
3308
3309 return QVariant::fromValue( QgsGeometry( mp ) );
3310}
3311
3312static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3313{
3314 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3315
3316 if ( geom.isNull() )
3317 return QVariant();
3318
3319 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3320
3321 //OK, now we have a complete list of segmentized lines from the geometry
3323 for ( QgsLineString *line : linesToProcess )
3324 {
3325 for ( int i = 0; i < line->numPoints() - 1; ++i )
3326 {
3328 segment->setPoints( QgsPointSequence()
3329 << line->pointN( i )
3330 << line->pointN( i + 1 ) );
3331 ml->addGeometry( segment );
3332 }
3333 delete line;
3334 }
3335
3336 return QVariant::fromValue( QgsGeometry( ml ) );
3337}
3338
3339static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3340{
3341 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3342
3343 if ( geom.isNull() )
3344 return QVariant();
3345
3346 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3347 if ( !curvePolygon && geom.isMultipart() )
3348 {
3349 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3350 {
3351 if ( collection->numGeometries() == 1 )
3352 {
3353 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3354 }
3355 }
3356 }
3357
3358 if ( !curvePolygon )
3359 return QVariant();
3360
3361 //idx is 1 based
3362 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3363
3364 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3365 return QVariant();
3366
3367 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3368 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3369 return result;
3370}
3371
3372static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3373{
3374 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3375
3376 if ( geom.isNull() )
3377 return QVariant();
3378
3379 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3380 if ( !collection )
3381 return QVariant();
3382
3383 //idx is 1 based
3384 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3385
3386 if ( idx < 0 || idx >= collection->numGeometries() )
3387 return QVariant();
3388
3389 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3390 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3391 return result;
3392}
3393
3394static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3395{
3396 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3397
3398 if ( geom.isNull() )
3399 return QVariant();
3400
3401 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3402 if ( !boundary )
3403 return QVariant();
3404
3405 return QVariant::fromValue( QgsGeometry( boundary ) );
3406}
3407
3408static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3409{
3410 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3411
3412 if ( geom.isNull() )
3413 return QVariant();
3414
3415 QgsGeometry merged = geom.mergeLines();
3416 if ( merged.isNull() )
3417 return QVariant();
3418
3419 return QVariant::fromValue( merged );
3420}
3421
3422static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3423{
3424 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3425 if ( geom.isNull() )
3426 return QVariant();
3427
3428 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3429 if ( geom2.isNull() )
3430 return QVariant();
3431
3432 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3433 if ( sharedPaths.isNull() )
3434 return QVariant();
3435
3436 return QVariant::fromValue( sharedPaths );
3437}
3438
3439
3440static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3441{
3442 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3443
3444 if ( geom.isNull() )
3445 return QVariant();
3446
3447 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3448
3449 QgsGeometry simplified = geom.simplify( tolerance );
3450 if ( simplified.isNull() )
3451 return QVariant();
3452
3453 return simplified;
3454}
3455
3456static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3457{
3458 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3459
3460 if ( geom.isNull() )
3461 return QVariant();
3462
3463 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3464
3466
3467 QgsGeometry simplified = simplifier.simplify( geom );
3468 if ( simplified.isNull() )
3469 return QVariant();
3470
3471 return simplified;
3472}
3473
3474static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3475{
3476 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3477
3478 if ( geom.isNull() )
3479 return QVariant();
3480
3481 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3482 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3483 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3484 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3485
3486 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3487 if ( smoothed.isNull() )
3488 return QVariant();
3489
3490 return smoothed;
3491}
3492
3493static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3494{
3495 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3496
3497 if ( geom.isNull() )
3498 return QVariant();
3499
3500 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3501 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3502 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3503
3504 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3505 if ( waved.isNull() )
3506 return QVariant();
3507
3508 return waved;
3509}
3510
3511static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3512{
3513 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3514
3515 if ( geom.isNull() )
3516 return QVariant();
3517
3518 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3519 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3520 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3521 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3522 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3523
3524 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3525 minAmplitude, maxAmplitude, seed );
3526 if ( waved.isNull() )
3527 return QVariant();
3528
3529 return waved;
3530}
3531
3532static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3533{
3534 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3535
3536 if ( geom.isNull() )
3537 return QVariant();
3538
3539 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3540 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3541 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3542
3543 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3544 if ( waved.isNull() )
3545 return QVariant();
3546
3547 return waved;
3548}
3549
3550static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3551{
3552 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3553
3554 if ( geom.isNull() )
3555 return QVariant();
3556
3557 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3558 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3559 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3560 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3561 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3562
3563 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3564 minAmplitude, maxAmplitude, seed );
3565 if ( waved.isNull() )
3566 return QVariant();
3567
3568 return waved;
3569}
3570
3571static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3572{
3573 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3574
3575 if ( geom.isNull() )
3576 return QVariant();
3577
3578 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3579 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3580 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3581
3582 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3583 if ( waved.isNull() )
3584 return QVariant();
3585
3586 return waved;
3587}
3588
3589static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3590{
3591 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3592
3593 if ( geom.isNull() )
3594 return QVariant();
3595
3596 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3597 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3598 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3599 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3600 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3601
3602 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3603 minAmplitude, maxAmplitude, seed );
3604 if ( waved.isNull() )
3605 return QVariant();
3606
3607 return waved;
3608}
3609
3610static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3611{
3612 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3613
3614 if ( geom.isNull() )
3615 return QVariant();
3616
3617 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3618 QVector< double > dashPattern;
3619 dashPattern.reserve( pattern.size() );
3620 for ( const QVariant &value : std::as_const( pattern ) )
3621 {
3622 bool ok = false;
3623 double v = value.toDouble( &ok );
3624 if ( ok )
3625 {
3626 dashPattern << v;
3627 }
3628 else
3629 {
3630 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3631 return QgsGeometry();
3632 }
3633 }
3634
3635 if ( dashPattern.size() % 2 != 0 )
3636 {
3637 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3638 return QgsGeometry();
3639 }
3640
3641 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3643 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3645 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3647 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3649 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3651 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3653 else
3654 {
3655 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3656 return QgsGeometry();
3657 }
3658
3659 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3661 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3663 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3665 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3667 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3669 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3671 else
3672 {
3673 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3674 return QgsGeometry();
3675 }
3676
3677 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3679 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3681 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3683 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3685 else
3686 {
3687 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3688 return QgsGeometry();
3689 }
3690
3691 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3692
3693 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3694 if ( result.isNull() )
3695 return QVariant();
3696
3697 return result;
3698}
3699
3700static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3701{
3702 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3703
3704 if ( geom.isNull() )
3705 return QVariant();
3706
3707 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3708 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3709 if ( densified.isNull() )
3710 return QVariant();
3711
3712 return densified;
3713}
3714
3715static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3716{
3717 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3718
3719 if ( geom.isNull() )
3720 return QVariant();
3721
3722 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3723 const QgsGeometry densified = geom.densifyByDistance( distance );
3724 if ( densified.isNull() )
3725 return QVariant();
3726
3727 return densified;
3728}
3729
3730static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3731{
3732 QVariantList list;
3733 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3734 {
3735 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3736 }
3737 else
3738 {
3739 list = values;
3740 }
3741
3742 QVector< QgsGeometry > parts;
3743 parts.reserve( list.size() );
3744 for ( const QVariant &value : std::as_const( list ) )
3745 {
3746 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
3747 if ( part.isNull() )
3748 return QgsGeometry();
3749 parts << part;
3750 }
3751
3752 return QgsGeometry::collectGeometry( parts );
3753}
3754
3755static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3756{
3757 if ( values.count() < 2 || values.count() > 4 )
3758 {
3759 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3760 return QVariant();
3761 }
3762
3763 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3764 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3765 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3766 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3767 switch ( values.count() )
3768 {
3769 case 2:
3770 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3771 case 3:
3772 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3773 case 4:
3774 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3775 }
3776 return QVariant(); //avoid warning
3777}
3778
3779static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3780{
3781 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3782 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3783 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3784 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3785}
3786
3787static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3788{
3789 if ( values.empty() )
3790 {
3791 return QVariant();
3792 }
3793
3794 QVector<QgsPoint> points;
3795 points.reserve( values.count() );
3796
3797 auto addPoint = [&points]( const QgsGeometry & geom )
3798 {
3799 if ( geom.isNull() )
3800 return;
3801
3802 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3803 return;
3804
3805 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3806 if ( !point )
3807 return;
3808
3809 points << *point;
3810 };
3811
3812 for ( const QVariant &value : values )
3813 {
3814 if ( value.userType() == QMetaType::Type::QVariantList )
3815 {
3816 const QVariantList list = value.toList();
3817 for ( const QVariant &v : list )
3818 {
3819 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3820 }
3821 }
3822 else
3823 {
3824 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3825 }
3826 }
3827
3828 if ( points.count() < 2 )
3829 return QVariant();
3830
3831 return QgsGeometry( new QgsLineString( points ) );
3832}
3833
3834static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3835{
3836 if ( values.count() < 1 )
3837 {
3838 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3839 return QVariant();
3840 }
3841
3842 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3843
3844 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3845 return outerRing; // if it's already a polygon we have nothing to do
3846
3847 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3848 return QVariant();
3849
3850 auto polygon = std::make_unique< QgsPolygon >();
3851
3852 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
3853 if ( !exteriorRing && outerRing.isMultipart() )
3854 {
3855 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3856 {
3857 if ( collection->numGeometries() == 1 )
3858 {
3859 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3860 }
3861 }
3862 }
3863
3864 if ( !exteriorRing )
3865 return QVariant();
3866
3867 polygon->setExteriorRing( exteriorRing->segmentize() );
3868
3869
3870 for ( int i = 1; i < values.count(); ++i )
3871 {
3872 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3873 if ( ringGeom.isNull() )
3874 continue;
3875
3876 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3877 continue;
3878
3879 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
3880 if ( !ring && ringGeom.isMultipart() )
3881 {
3882 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3883 {
3884 if ( collection->numGeometries() == 1 )
3885 {
3886 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
3887 }
3888 }
3889 }
3890
3891 if ( !ring )
3892 continue;
3893
3894 polygon->addInteriorRing( ring->segmentize() );
3895 }
3896
3897 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3898}
3899
3900static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3901{
3902 auto tr = std::make_unique<QgsTriangle>();
3903 auto lineString = std::make_unique<QgsLineString>();
3904 lineString->clear();
3905
3906 for ( const QVariant &value : values )
3907 {
3908 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3909 if ( geom.isNull() )
3910 return QVariant();
3911
3912 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3913 return QVariant();
3914
3915 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3916 if ( !point && geom.isMultipart() )
3917 {
3918 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3919 {
3920 if ( collection->numGeometries() == 1 )
3921 {
3922 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3923 }
3924 }
3925 }
3926
3927 if ( !point )
3928 return QVariant();
3929
3930 lineString->addVertex( *point );
3931 }
3932
3933 tr->setExteriorRing( lineString.release() );
3934
3935 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3936}
3937
3938static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3939{
3940 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3941 if ( geom.isNull() )
3942 return QVariant();
3943
3944 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3945 return QVariant();
3946
3947 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3948 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3949
3950 if ( segment < 3 )
3951 {
3952 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3953 return QVariant();
3954 }
3955 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3956 if ( !point && geom.isMultipart() )
3957 {
3958 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3959 {
3960 if ( collection->numGeometries() == 1 )
3961 {
3962 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3963 }
3964 }
3965 }
3966 if ( !point )
3967 return QVariant();
3968
3969 QgsCircle circ( *point, radius );
3970 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3971}
3972
3973static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3974{
3975 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3976 if ( geom.isNull() )
3977 return QVariant();
3978
3979 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3980 return QVariant();
3981
3982 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3983 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3984 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3985 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3986 if ( segment < 3 )
3987 {
3988 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3989 return QVariant();
3990 }
3991 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3992 if ( !point && geom.isMultipart() )
3993 {
3994 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3995 {
3996 if ( collection->numGeometries() == 1 )
3997 {
3998 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3999 }
4000 }
4001 }
4002 if ( !point )
4003 return QVariant();
4004
4005 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4006 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4007}
4008
4009static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4010{
4011
4012 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4013 if ( pt1.isNull() )
4014 return QVariant();
4015
4016 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4017 return QVariant();
4018
4019 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4020 if ( pt2.isNull() )
4021 return QVariant();
4022
4023 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4024 return QVariant();
4025
4026 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4027 if ( nbEdges < 3 )
4028 {
4029 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4030 return QVariant();
4031 }
4032
4033 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4035 {
4036 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4037 return QVariant();
4038 }
4039
4040 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
4041 if ( !center && pt1.isMultipart() )
4042 {
4043 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
4044 {
4045 if ( collection->numGeometries() == 1 )
4046 {
4047 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4048 }
4049 }
4050 }
4051 if ( !center )
4052 return QVariant();
4053
4054 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
4055 if ( !corner && pt2.isMultipart() )
4056 {
4057 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
4058 {
4059 if ( collection->numGeometries() == 1 )
4060 {
4061 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4062 }
4063 }
4064 }
4065 if ( !corner )
4066 return QVariant();
4067
4068 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4069
4070 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4071
4072}
4073
4074static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4075{
4076 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4077 if ( pt1.isNull() )
4078 return QVariant();
4079 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4080 return QVariant();
4081
4082 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4083 if ( pt2.isNull() )
4084 return QVariant();
4085 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4086 return QVariant();
4087
4088 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4089 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4090 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4091
4092 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4093}
4094
4095static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4096{
4097 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4098 if ( pt1.isNull() )
4099 return QVariant();
4100 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4101 return QVariant();
4102
4103 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4104 if ( pt2.isNull() )
4105 return QVariant();
4106 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4107 return QVariant();
4108
4109 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4110 if ( pt3.isNull() )
4111 return QVariant();
4112 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4113 return QVariant();
4114
4115 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4116 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4117 {
4118 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4119 return QVariant();
4120 }
4121 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4122 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4123 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4124 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4125 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4126}
4127
4128static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4129{
4130 if ( geom.isNull() )
4131 return QVariant();
4132
4133 if ( idx < 0 )
4134 {
4135 idx += geom.constGet()->nCoordinates();
4136 }
4137 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4138 {
4139 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4140 return QVariant();
4141 }
4142 return QVariant::fromValue( geom.vertexAt( idx ) );
4143}
4144
4145// function used for the old $ style
4146static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4147{
4148 FEAT_FROM_CONTEXT( context, feature )
4149 const QgsGeometry geom = feature.geometry();
4150 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4151
4152 const QVariant v = pointAt( geom, idx, parent );
4153
4154 if ( !v.isNull() )
4155 return QVariant( v.value<QgsPoint>().x() );
4156 else
4157 return QVariant();
4158}
4159static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4160{
4161 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))
4162 {
4163 return fcnOldXat( values, f, parent, node );
4164 }
4165 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)
4166 {
4167 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4168 }
4169
4170 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4171 if ( geom.isNull() )
4172 {
4173 return QVariant();
4174 }
4175
4176 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4177
4178 const QVariant v = pointAt( geom, vertexNumber, parent );
4179 if ( !v.isNull() )
4180 return QVariant( v.value<QgsPoint>().x() );
4181 else
4182 return QVariant();
4183}
4184
4185// function used for the old $ style
4186static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4187{
4188 FEAT_FROM_CONTEXT( context, feature )
4189 const QgsGeometry geom = feature.geometry();
4190 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4191
4192 const QVariant v = pointAt( geom, idx, parent );
4193
4194 if ( !v.isNull() )
4195 return QVariant( v.value<QgsPoint>().y() );
4196 else
4197 return QVariant();
4198}
4199static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4200{
4201 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))
4202 {
4203 return fcnOldYat( values, f, parent, node );
4204 }
4205 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)
4206 {
4207 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4208 }
4209
4210 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4211 if ( geom.isNull() )
4212 {
4213 return QVariant();
4214 }
4215
4216 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4217
4218 const QVariant v = pointAt( geom, vertexNumber, parent );
4219 if ( !v.isNull() )
4220 return QVariant( v.value<QgsPoint>().y() );
4221 else
4222 return QVariant();
4223}
4224
4225static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4226{
4227 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4228 if ( geom.isNull() )
4229 {
4230 return QVariant();
4231 }
4232
4233 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4234
4235 const QVariant v = pointAt( geom, vertexNumber, parent );
4236 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4237 return QVariant( v.value<QgsPoint>().z() );
4238 else
4239 return QVariant();
4240}
4241
4242static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4243{
4244 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4245 if ( geom.isNull() )
4246 {
4247 return QVariant();
4248 }
4249
4250 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4251
4252 const QVariant v = pointAt( geom, vertexNumber, parent );
4253 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4254 return QVariant( v.value<QgsPoint>().m() );
4255 else
4256 return QVariant();
4257}
4258
4259
4260static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4261{
4262 if ( !context )
4263 return QVariant();
4264
4265 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4266 if ( context->hasGeometry() )
4267 return context->geometry();
4268 else
4269 {
4270 FEAT_FROM_CONTEXT( context, f )
4271 QgsGeometry geom = f.geometry();
4272 if ( !geom.isNull() )
4273 return QVariant::fromValue( geom );
4274 else
4275 return QVariant();
4276 }
4277}
4278
4279static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4280{
4281 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4282 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4283 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4284 return result;
4285}
4286
4287static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4288{
4289 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4290 if ( wkb.isNull() )
4291 return QVariant();
4292
4293 QgsGeometry geom;
4294 geom.fromWkb( wkb );
4295 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4296}
4297
4298static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4299{
4300 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4301 QgsOgcUtils::Context ogcContext;
4302 if ( context )
4303 {
4304 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4305 if ( mapLayerPtr )
4306 {
4307 ogcContext.layer = mapLayerPtr.data();
4308 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4309 }
4310 }
4311 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4312 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4313 return result;
4314}
4315
4316static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4317{
4318 FEAT_FROM_CONTEXT( context, f )
4320 QgsDistanceArea *calc = parent->geomCalculator();
4321 if ( calc )
4322 {
4323 try
4324 {
4325 double area = calc->measureArea( f.geometry() );
4326 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4327 return QVariant( area );
4328 }
4329 catch ( QgsCsException & )
4330 {
4331 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4332 return QVariant();
4333 }
4334 }
4335 else
4336 {
4337 return QVariant( f.geometry().area() );
4338 }
4339}
4340
4341static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4342{
4343 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4344
4345 if ( geom.type() != Qgis::GeometryType::Polygon )
4346 return QVariant();
4347
4348 return QVariant( geom.area() );
4349}
4350
4351static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4352{
4353 FEAT_FROM_CONTEXT( context, f )
4355 QgsDistanceArea *calc = parent->geomCalculator();
4356 if ( calc )
4357 {
4358 try
4359 {
4360 double len = calc->measureLength( f.geometry() );
4361 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4362 return QVariant( len );
4363 }
4364 catch ( QgsCsException & )
4365 {
4366 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4367 return QVariant();
4368 }
4369 }
4370 else
4371 {
4372 return QVariant( f.geometry().length() );
4373 }
4374}
4375
4376static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4377{
4378 FEAT_FROM_CONTEXT( context, f )
4380 QgsDistanceArea *calc = parent->geomCalculator();
4381 if ( calc )
4382 {
4383 try
4384 {
4385 double len = calc->measurePerimeter( f.geometry() );
4386 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4387 return QVariant( len );
4388 }
4389 catch ( QgsCsException & )
4390 {
4391 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4392 return QVariant();
4393 }
4394 }
4395 else
4396 {
4397 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4398 }
4399}
4400
4401static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4402{
4403 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4404
4405 if ( geom.type() != Qgis::GeometryType::Polygon )
4406 return QVariant();
4407
4408 //length for polygons = perimeter
4409 return QVariant( geom.length() );
4410}
4411
4412static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4413{
4414 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4415 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4416}
4417
4418static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4419{
4420 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4421 if ( geom.isNull() )
4422 return QVariant();
4423
4424 return QVariant( geom.constGet()->partCount() );
4425}
4426
4427static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4428{
4429 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4430 if ( geom.isNull() )
4431 return QVariant();
4432
4433 return QVariant( geom.isMultipart() );
4434}
4435
4436static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4437{
4438 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4439
4440 if ( geom.isNull() )
4441 return QVariant();
4442
4443 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4444 if ( curvePolygon )
4445 return QVariant( curvePolygon->numInteriorRings() );
4446
4447 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4448 if ( collection )
4449 {
4450 //find first CurvePolygon in collection
4451 for ( int i = 0; i < collection->numGeometries(); ++i )
4452 {
4453 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4454 if ( !curvePolygon )
4455 continue;
4456
4457 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4458 }
4459 }
4460
4461 return QVariant();
4462}
4463
4464static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4465{
4466 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4467
4468 if ( geom.isNull() )
4469 return QVariant();
4470
4471 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4472 if ( curvePolygon )
4473 return QVariant( curvePolygon->ringCount() );
4474
4475 bool foundPoly = false;
4476 int ringCount = 0;
4477 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4478 if ( collection )
4479 {
4480 //find CurvePolygons in collection
4481 for ( int i = 0; i < collection->numGeometries(); ++i )
4482 {
4483 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4484 if ( !curvePolygon )
4485 continue;
4486
4487 foundPoly = true;
4488 ringCount += curvePolygon->ringCount();
4489 }
4490 }
4491
4492 if ( !foundPoly )
4493 return QVariant();
4494
4495 return QVariant( ringCount );
4496}
4497
4498static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4499{
4500 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4501 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4502 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4503 return result;
4504}
4505
4506static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4507{
4508 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4509 return QVariant::fromValue( geom.boundingBox().width() );
4510}
4511
4512static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4513{
4514 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4515 return QVariant::fromValue( geom.boundingBox().height() );
4516}
4517
4518static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4519{
4520 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4521 if ( geom.isNull() )
4522 return QVariant();
4523
4525}
4526
4527static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4528{
4529 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4530 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4531}
4532
4533static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4534{
4535 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4536 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4537}
4538
4539static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4540{
4541 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4542 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4543}
4544
4545static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4546{
4547 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4548 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4549}
4550
4551static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4552{
4553 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4554
4555 if ( geom.isNull() || geom.isEmpty( ) )
4556 return QVariant();
4557
4558 if ( !geom.constGet()->is3D() )
4559 return QVariant();
4560
4561 double max = std::numeric_limits< double >::lowest();
4562
4563 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4564 {
4565 double z = ( *it ).z();
4566
4567 if ( max < z )
4568 max = z;
4569 }
4570
4571 if ( max == std::numeric_limits< double >::lowest() )
4572 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4573
4574 return QVariant( max );
4575}
4576
4577static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4578{
4579 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4580
4581 if ( geom.isNull() || geom.isEmpty() )
4582 return QVariant();
4583
4584 if ( !geom.constGet()->is3D() )
4585 return QVariant();
4586
4587 double min = std::numeric_limits< double >::max();
4588
4589 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4590 {
4591 double z = ( *it ).z();
4592
4593 if ( z < min )
4594 min = z;
4595 }
4596
4597 if ( min == std::numeric_limits< double >::max() )
4598 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4599
4600 return QVariant( min );
4601}
4602
4603static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4604{
4605 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4606
4607 if ( geom.isNull() || geom.isEmpty() )
4608 return QVariant();
4609
4610 if ( !geom.constGet()->isMeasure() )
4611 return QVariant();
4612
4613 double min = std::numeric_limits< double >::max();
4614
4615 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4616 {
4617 double m = ( *it ).m();
4618
4619 if ( m < min )
4620 min = m;
4621 }
4622
4623 if ( min == std::numeric_limits< double >::max() )
4624 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4625
4626 return QVariant( min );
4627}
4628
4629static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4630{
4631 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4632
4633 if ( geom.isNull() || geom.isEmpty() )
4634 return QVariant();
4635
4636 if ( !geom.constGet()->isMeasure() )
4637 return QVariant();
4638
4639 double max = std::numeric_limits< double >::lowest();
4640
4641 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4642 {
4643 double m = ( *it ).m();
4644
4645 if ( max < m )
4646 max = m;
4647 }
4648
4649 if ( max == std::numeric_limits< double >::lowest() )
4650 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4651
4652 return QVariant( max );
4653}
4654
4655static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4656{
4657 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4658 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4659 if ( !curve )
4660 {
4661 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4662 return QVariant();
4663 }
4664
4665 return QVariant( curve->sinuosity() );
4666}
4667
4668static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4669{
4670 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4671 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4672 if ( !curve )
4673 {
4674 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4675 return QVariant();
4676 }
4677
4678 return QVariant( curve->straightDistance2d() );
4679}
4680
4681static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4682{
4683 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4684 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4685
4686 if ( !poly )
4687 {
4688 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4689 return QVariant();
4690 }
4691
4692 return QVariant( poly->roundness() );
4693}
4694
4695
4696
4697static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4698{
4699 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4700 if ( geom.isNull() )
4701 return QVariant();
4702
4703 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4704 flipped->swapXy();
4705 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4706}
4707
4708static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4709{
4710 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4711 if ( fGeom.isNull() )
4712 return QVariant();
4713
4714 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4715 if ( !curve && fGeom.isMultipart() )
4716 {
4717 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4718 {
4719 if ( collection->numGeometries() == 1 )
4720 {
4721 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4722 }
4723 }
4724 }
4725
4726 if ( !curve )
4727 return QVariant();
4728
4729 return QVariant::fromValue( curve->isClosed() );
4730}
4731
4732static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4733{
4734 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4735
4736 if ( geom.isNull() )
4737 return QVariant();
4738
4739 QVariant result;
4740 if ( !geom.isMultipart() )
4741 {
4742 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4743
4744 if ( !line )
4745 return QVariant();
4746
4747 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4748 closedLine->close();
4749
4750 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4751 }
4752 else
4753 {
4754 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4755 if ( !collection )
4756 return QVariant();
4757
4758 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4759
4760 for ( int i = 0; i < collection->numGeometries(); ++i )
4761 {
4762 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4763 {
4764 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4765 closedLine->close();
4766
4767 closed->addGeometry( closedLine.release() );
4768 }
4769 }
4770 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4771 }
4772
4773 return result;
4774}
4775
4776static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4777{
4778 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4779 if ( fGeom.isNull() )
4780 return QVariant();
4781
4782 return QVariant::fromValue( fGeom.isEmpty() );
4783}
4784
4785static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4786{
4787 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4788 return QVariant::fromValue( true );
4789
4790 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4791 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4792}
4793
4794static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4795{
4796 if ( values.length() < 2 || values.length() > 3 )
4797 return QVariant();
4798
4799 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4800 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4801
4802 if ( fGeom.isNull() || sGeom.isNull() )
4803 return QVariant();
4804
4805 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4806
4807 if ( values.length() == 2 )
4808 {
4809 //two geometry arguments, return relation
4810 QString result = engine->relate( sGeom.constGet() );
4811 return QVariant::fromValue( result );
4812 }
4813 else
4814 {
4815 //three arguments, test pattern
4816 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4817 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4818 return QVariant::fromValue( result );
4819 }
4820}
4821
4822static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4823{
4824 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4825 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4826 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4827}
4828static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4829{
4830 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4831 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4832 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4833}
4834static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4835{
4836 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4837 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4838 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4839}
4840static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4841{
4842 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4843 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4844 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4845}
4846static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4847{
4848 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4849 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4850 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4851}
4852static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4853{
4854 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4855 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4856 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4857}
4858static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4859{
4860 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4861 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4862 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4863}
4864static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4865{
4866 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4867 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4868 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4869}
4870
4871static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4872{
4873 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4874 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4875 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4876 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4877 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4878 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4879
4881 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4882 capStyle = Qgis::EndCapStyle::Flat;
4883 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4884 capStyle = Qgis::EndCapStyle::Square;
4885
4887 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4888 joinStyle = Qgis::JoinStyle::Miter;
4889 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4890 joinStyle = Qgis::JoinStyle::Bevel;
4891
4892 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4893 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4894 return result;
4895}
4896
4897static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4898{
4899 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4900 const QgsGeometry reoriented = fGeom.forceRHR();
4901 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4902}
4903
4904static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4905{
4906 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4907 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4908 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4909}
4910
4911static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4912{
4913 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4914 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4915 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4916}
4917
4918static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4919{
4920 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4921 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4922 if ( !pt && fGeom.isMultipart() )
4923 {
4924 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4925 {
4926 if ( collection->numGeometries() == 1 )
4927 {
4928 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4929 }
4930 }
4931 }
4932
4933 if ( !pt )
4934 {
4935 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4936 return QVariant();
4937 }
4938
4939 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4940 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4941 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4942 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4943
4944 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4945 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4946 return result;
4947}
4948
4949static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4950{
4951 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4952 if ( fGeom.type() != Qgis::GeometryType::Line )
4953 {
4954 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4955 return QVariant();
4956 }
4957
4958 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4959 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4960 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4961
4962 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4963 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4964 return result;
4965}
4966
4967static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4968{
4969 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4970 if ( fGeom.type() != Qgis::GeometryType::Line )
4971 {
4972 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4973 return QVariant();
4974 }
4975
4976 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4977
4978 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4979 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4980 return result;
4981}
4982
4983static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4984{
4985 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4986 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4987 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4988 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4989 if ( joinInt < 1 || joinInt > 3 )
4990 return QVariant();
4991 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4992
4993 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4994
4995 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4996 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4997 return result;
4998}
4999
5000static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5001{
5002 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5003 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5004 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5005
5006 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5007 if ( joinInt < 1 || joinInt > 3 )
5008 return QVariant();
5009 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5010
5011 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5012
5013 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5014 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5015 return result;
5016}
5017
5018static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5019{
5020 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5021 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5022 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5023
5024 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5025 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5026 return result;
5027}
5028
5029static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5030{
5031 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5032 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5033 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5034 fGeom.translate( dx, dy );
5035 return QVariant::fromValue( fGeom );
5036}
5037
5038static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5039{
5040 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5041 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5042 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5043 : QgsGeometry();
5044 const bool perPart = values.value( 3 ).toBool();
5045
5046 if ( center.isNull() && perPart && fGeom.isMultipart() )
5047 {
5048 // no explicit center, rotating per part
5049 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5050 // the result is equivalent to setting perPart as false anyway)
5051 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5052 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5053 {
5054 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5055 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5056 t.rotate( -rotation );
5057 t.translate( -partCenter.x(), -partCenter.y() );
5058 ( *it )->transform( t );
5059 }
5060 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5061 }
5062 else
5063 {
5064 QgsPointXY pt;
5065 if ( center.isEmpty() )
5066 {
5067 // if center wasn't specified, use bounding box centroid
5068 pt = fGeom.boundingBox().center();
5069 }
5071 {
5072 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5073 return QVariant();
5074 }
5075 else
5076 {
5077 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
5078 }
5079
5080 fGeom.rotate( rotation, pt );
5081 return QVariant::fromValue( fGeom );
5082 }
5083}
5084
5085static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5086{
5087 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5088 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5089 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5090 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5091 : QgsGeometry();
5092
5093 QgsPointXY pt;
5094 if ( center.isNull() )
5095 {
5096 // if center wasn't specified, use bounding box centroid
5097 pt = fGeom.boundingBox().center();
5098 }
5100 {
5101 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5102 return QVariant();
5103 }
5104 else
5105 {
5106 pt = center.asPoint();
5107 }
5108
5109 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5110 t.scale( xScale, yScale );
5111 t.translate( -pt.x(), -pt.y() );
5112 fGeom.transform( t );
5113 return QVariant::fromValue( fGeom );
5114}
5115
5116static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5117{
5118 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5119 if ( fGeom.isNull() )
5120 {
5121 return QVariant();
5122 }
5123
5124 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5125 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5126
5127 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5128
5129 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5130 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5131
5132 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5133 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5134 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5135 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5136
5137 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5138 {
5139 fGeom.get()->addZValue( 0 );
5140 }
5141 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5142 {
5143 fGeom.get()->addMValue( 0 );
5144 }
5145
5146 QTransform transform;
5147 transform.translate( deltaX, deltaY );
5148 transform.rotate( rotationZ );
5149 transform.scale( scaleX, scaleY );
5150 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5151
5152 return QVariant::fromValue( fGeom );
5153}
5154
5155
5156static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5157{
5158 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5159 QgsGeometry geom = fGeom.centroid();
5160 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5161 return result;
5162}
5163static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5164{
5165 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5166 QgsGeometry geom = fGeom.pointOnSurface();
5167 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5168 return result;
5169}
5170
5171static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5172{
5173 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5174 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5175 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5176 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5177 return result;
5178}
5179
5180static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5181{
5182 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5183 QgsGeometry geom = fGeom.convexHull();
5184 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5185 return result;
5186}
5187
5188#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5189static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5190{
5191 try
5192 {
5193 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5194 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5195 const bool allowHoles = values.value( 2 ).toBool();
5196 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5197 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5198 return result;
5199 }
5200 catch ( QgsCsException &cse )
5201 {
5202 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5203 return QVariant();
5204 }
5205}
5206#endif
5207
5208static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5209{
5210 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5211 int segments = 36;
5212 if ( values.length() == 2 )
5213 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5214 if ( segments < 0 )
5215 {
5216 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5217 return QVariant();
5218 }
5219
5220 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5221 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5222 return result;
5223}
5224
5225static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5226{
5227 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5229 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5230 return result;
5231}
5232
5233static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5234{
5235 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5236
5237 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5238 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5239 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5240
5241 double area, angle, width, height;
5242 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5243
5244 if ( geom.isNull() )
5245 {
5246 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5247 return QVariant();
5248 }
5249 return angle;
5250}
5251
5252static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5253{
5254 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5255 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5256 QgsGeometry geom = fGeom.difference( sGeom );
5257 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5258 return result;
5259}
5260
5261static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5262{
5263 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5264 return QVariant();
5265
5266 // two variants, one for geometry, one for string
5267
5268 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5269 if ( !fGeom.isNull() )
5270 {
5271 QVariant result;
5272 if ( !fGeom.isMultipart() )
5273 {
5274 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5275 if ( !curve )
5276 return QVariant();
5277
5278 QgsCurve *reversed = curve->reversed();
5279 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5280 }
5281 else
5282 {
5283 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5284 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5285 for ( int i = 0; i < collection->numGeometries(); ++i )
5286 {
5287 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5288 {
5289 reversed->addGeometry( curve->reversed() );
5290 }
5291 else
5292 {
5293 reversed->addGeometry( collection->geometryN( i )->clone() );
5294 }
5295 }
5296 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5297 }
5298 return result;
5299 }
5300
5301 //fall back to string variant
5302 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5303 std::reverse( string.begin(), string.end() );
5304 return string;
5305}
5306
5307static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5308{
5309 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5310 if ( fGeom.isNull() )
5311 return QVariant();
5312
5313 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5314 if ( !curvePolygon && fGeom.isMultipart() )
5315 {
5316 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5317 {
5318 if ( collection->numGeometries() == 1 )
5319 {
5320 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5321 }
5322 }
5323 }
5324
5325 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5326 return QVariant();
5327
5328 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5329 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5330 return result;
5331}
5332
5333static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5334{
5335 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5336 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5337 return QVariant( fGeom.distance( sGeom ) );
5338}
5339
5340static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5341{
5342 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5343 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5344
5345 double res = -1;
5346 if ( values.length() == 3 && values.at( 2 ).isValid() )
5347 {
5348 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5349 densify = std::clamp( densify, 0.0, 1.0 );
5350 res = g1.hausdorffDistanceDensify( g2, densify );
5351 }
5352 else
5353 {
5354 res = g1.hausdorffDistance( g2 );
5355 }
5356
5357 return res > -1 ? QVariant( res ) : QVariant();
5358}
5359
5360static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5361{
5362 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5363 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5364 QgsGeometry geom = fGeom.intersection( sGeom );
5365 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5366 return result;
5367}
5368static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5369{
5370 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5371 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5372 QgsGeometry geom = fGeom.symDifference( sGeom );
5373 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5374 return result;
5375}
5376static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5377{
5378 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5379 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5380 QgsGeometry geom = fGeom.combine( sGeom );
5381 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5382 return result;
5383}
5384
5385static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5386{
5387 if ( values.length() < 1 || values.length() > 2 )
5388 return QVariant();
5389
5390 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5391 int prec = 8;
5392 if ( values.length() == 2 )
5393 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5394 QString wkt = fGeom.asWkt( prec );
5395 return QVariant( wkt );
5396}
5397
5398static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5399{
5400 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5401 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5402}
5403
5404static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5405{
5406 if ( values.length() != 2 )
5407 {
5408 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5409 return QVariant();
5410 }
5411
5412 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5413 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5414
5415 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5416 if ( !pt1 && fGeom1.isMultipart() )
5417 {
5418 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5419 {
5420 if ( collection->numGeometries() == 1 )
5421 {
5422 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5423 }
5424 }
5425 }
5426
5427 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5428 if ( !pt2 && fGeom2.isMultipart() )
5429 {
5430 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5431 {
5432 if ( collection->numGeometries() == 1 )
5433 {
5434 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5435 }
5436 }
5437 }
5438
5439 if ( !pt1 || !pt2 )
5440 {
5441 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5442 return QVariant();
5443 }
5444
5445 // Code from PostGIS
5446 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5447 {
5448 if ( pt1->y() < pt2->y() )
5449 return 0.0;
5450 else if ( pt1->y() > pt2->y() )
5451 return M_PI;
5452 else
5453 return 0;
5454 }
5455
5456 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5457 {
5458 if ( pt1->x() < pt2->x() )
5459 return M_PI_2;
5460 else if ( pt1->x() > pt2->x() )
5461 return M_PI + ( M_PI_2 );
5462 else
5463 return 0;
5464 }
5465
5466 if ( pt1->x() < pt2->x() )
5467 {
5468 if ( pt1->y() < pt2->y() )
5469 {
5470 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5471 }
5472 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5473 {
5474 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5475 + ( M_PI_2 );
5476 }
5477 }
5478
5479 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5480 {
5481 if ( pt1->y() > pt2->y() )
5482 {
5483 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5484 + M_PI;
5485 }
5486 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5487 {
5488 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5489 + ( M_PI + ( M_PI_2 ) );
5490 }
5491 }
5492}
5493
5494static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5495{
5496 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5497 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5498 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
5499 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5500
5501 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5502 {
5503 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5504 return QVariant();
5505 }
5506
5507 const QgsPointXY point1 = geom1.asPoint();
5508 const QgsPointXY point2 = geom2.asPoint();
5509 if ( point1.isEmpty() || point2.isEmpty() )
5510 {
5511 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5512 return QVariant();
5513 }
5514
5516 if ( context )
5517 {
5518 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5519
5520 if ( !sourceCrs.isValid() )
5521 {
5522 sourceCrs = context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>();
5523 }
5524
5525 if ( ellipsoid.isEmpty() )
5526 {
5527 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5528 }
5529 }
5530
5531 if ( !sourceCrs.isValid() )
5532 {
5533 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5534 return QVariant();
5535 }
5536
5537 QgsDistanceArea da;
5538 da.setSourceCrs( sourceCrs, tContext );
5539 if ( !da.setEllipsoid( ellipsoid ) )
5540 {
5541 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5542 return QVariant();
5543 }
5544
5545 try
5546 {
5547 const double bearing = da.bearing( point1, point2 );
5548 if ( std::isfinite( bearing ) )
5549 {
5550 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5551 }
5552 }
5553 catch ( QgsCsException &cse )
5554 {
5555 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5556 return QVariant();
5557 }
5558 return QVariant();
5559}
5560
5561static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5562{
5563 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5564
5566 {
5567 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5568 return QVariant();
5569 }
5570
5571 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5572 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5573 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5574
5575 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5576 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5577
5578 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5579}
5580
5581static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5582{
5583 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5584 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5585
5586 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5587 if ( !pt1 && fGeom1.isMultipart() )
5588 {
5589 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5590 {
5591 if ( collection->numGeometries() == 1 )
5592 {
5593 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5594 }
5595 }
5596 }
5597 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5598 if ( !pt2 && fGeom2.isMultipart() )
5599 {
5600 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5601 {
5602 if ( collection->numGeometries() == 1 )
5603 {
5604 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5605 }
5606 }
5607 }
5608
5609 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5610 !pt1 || !pt2 )
5611 {
5612 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5613 return QVariant();
5614 }
5615
5616 return pt1->inclination( *pt2 );
5617
5618}
5619
5620static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5621{
5622 if ( values.length() != 3 )
5623 return QVariant();
5624
5625 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5626 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5627 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5628
5629 QgsGeometry geom = fGeom.extrude( x, y );
5630
5631 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5632 return result;
5633}
5634
5635static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5636{
5637 if ( values.length() < 2 )
5638 return QVariant();
5639
5640 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5641
5642 if ( !fGeom.isMultipart() )
5643 return values.at( 0 );
5644
5645 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5646 QVariant cachedExpression;
5647 if ( ctx )
5648 cachedExpression = ctx->cachedValue( expString );
5649 QgsExpression expression;
5650
5651 if ( cachedExpression.isValid() )
5652 {
5653 expression = cachedExpression.value<QgsExpression>();
5654 }
5655 else
5656 expression = QgsExpression( expString );
5657
5658 bool asc = values.value( 2 ).toBool();
5659
5660 QgsExpressionContext *unconstedContext = nullptr;
5661 QgsFeature f;
5662 if ( ctx )
5663 {
5664 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5665 // so no reason to worry
5666 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5667 f = ctx->feature();
5668 }
5669 else
5670 {
5671 // If there's no context provided, create a fake one
5672 unconstedContext = new QgsExpressionContext();
5673 }
5674
5675 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5676 Q_ASSERT( collection ); // Should have failed the multipart check above
5677
5679 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5680 QgsExpressionSorter sorter( orderBy );
5681
5682 QList<QgsFeature> partFeatures;
5683 partFeatures.reserve( collection->partCount() );
5684 for ( int i = 0; i < collection->partCount(); ++i )
5685 {
5686 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5687 partFeatures << f;
5688 }
5689
5690 sorter.sortFeatures( partFeatures, unconstedContext );
5691
5692 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5693
5694 Q_ASSERT( orderedGeom );
5695
5696 while ( orderedGeom->partCount() )
5697 orderedGeom->removeGeometry( 0 );
5698
5699 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5700 {
5701 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5702 }
5703
5704 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5705
5706 if ( !ctx )
5707 delete unconstedContext;
5708
5709 return result;
5710}
5711
5712static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5713{
5714 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5715 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5716
5717 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5718
5719 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5720 return result;
5721}
5722
5723static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5724{
5725 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5726 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5727
5728 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5729
5730 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5731 return result;
5732}
5733
5734static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5735{
5736 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5737 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5738
5739 QgsGeometry geom = lineGeom.interpolate( distance );
5740
5741 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5742 return result;
5743}
5744
5745static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5746{
5747 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5748 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5749 const bool use3DDistance = values.at( 2 ).toBool();
5750
5751 double x, y, z, distance;
5752
5753 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5754 if ( !line )
5755 {
5756 return QVariant();
5757 }
5758
5759 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
5760 {
5761 QgsPoint point( x, y );
5762 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
5763 {
5764 point.addZValue( z );
5765 }
5766 return QVariant::fromValue( QgsGeometry( point.clone() ) );
5767 }
5768
5769 return QVariant();
5770}
5771
5772static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5773{
5774 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5775 if ( lineGeom.type() != Qgis::GeometryType::Line )
5776 {
5777 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5778 return QVariant();
5779 }
5780
5781 const QgsCurve *curve = nullptr;
5782 if ( !lineGeom.isMultipart() )
5783 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5784 else
5785 {
5786 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5787 {
5788 if ( collection->numGeometries() > 0 )
5789 {
5790 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5791 }
5792 }
5793 }
5794 if ( !curve )
5795 return QVariant();
5796
5797 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5798 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5799
5800 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5801 QgsGeometry result( std::move( substring ) );
5802 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5803}
5804
5805static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5806{
5807 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5808 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5809
5810 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5811}
5812
5813static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5814{
5815 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5816 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5817 if ( vertex < 0 )
5818 {
5819 //negative idx
5820 int count = geom.constGet()->nCoordinates();
5821 vertex = count + vertex;
5822 }
5823
5824 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5825}
5826
5827static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5828{
5829 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5830 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5831 if ( vertex < 0 )
5832 {
5833 //negative idx
5834 int count = geom.constGet()->nCoordinates();
5835 vertex = count + vertex;
5836 }
5837
5838 return geom.distanceToVertex( vertex );
5839}
5840
5841static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5842{
5843 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5844 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5845
5846 double distance = lineGeom.lineLocatePoint( pointGeom );
5847
5848 return distance >= 0 ? distance : QVariant();
5849}
5850
5851static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5852{
5853 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5854 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5855 const bool use3DDistance = values.at( 2 ).toBool();
5856
5857 double x, y, z, distance;
5858
5859 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5860 if ( !line )
5861 {
5862 return QVariant();
5863 }
5864
5865 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
5866 return found ? distance : QVariant();
5867}
5868
5869static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5870{
5871 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5872 {
5873 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5874 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5875 }
5876
5877 if ( values.length() >= 1 )
5878 {
5879 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5880 return QVariant( qlonglong( std::round( number ) ) );
5881 }
5882
5883 return QVariant();
5884}
5885
5886static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5887{
5888 Q_UNUSED( values )
5889 Q_UNUSED( parent )
5890 return M_PI;
5891}
5892
5893static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5894{
5895 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5896 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5897 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5898 if ( places < 0 )
5899 {
5900 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5901 return QVariant();
5902 }
5903
5904 const bool omitGroupSeparator = values.value( 3 ).toBool();
5905 const bool trimTrailingZeros = values.value( 4 ).toBool();
5906
5907 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5908 if ( !omitGroupSeparator )
5909 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5910 else
5911 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5912
5913 QString res = locale.toString( value, 'f', places );
5914
5915 if ( trimTrailingZeros )
5916 {
5917#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5918 const QChar decimal = locale.decimalPoint();
5919 const QChar zeroDigit = locale.zeroDigit();
5920#else
5921 const QChar decimal = locale.decimalPoint().at( 0 );
5922 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5923#endif
5924
5925 if ( res.contains( decimal ) )
5926 {
5927 int trimPoint = res.length() - 1;
5928
5929 while ( res.at( trimPoint ) == zeroDigit )
5930 trimPoint--;
5931
5932 if ( res.at( trimPoint ) == decimal )
5933 trimPoint--;
5934
5935 res.truncate( trimPoint + 1 );
5936 }
5937 }
5938
5939 return res;
5940}
5941
5942static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5943{
5944 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5945 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5946 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5947
5948 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
5949 if ( format.indexOf( "Z" ) > 0 )
5950 datetime = datetime.toUTC();
5951
5952 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5953 return locale.toString( datetime, format );
5954}
5955
5956static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5957{
5958 const QVariant variant = values.at( 0 );
5959 bool isQColor;
5960 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
5961 if ( !color.isValid() )
5962 return QVariant();
5963
5964 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5965 if ( color.spec() == QColor::Spec::Cmyk )
5966 {
5967 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5968 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
5969 }
5970 else
5971 {
5972 const float avg = ( color.redF() + color.greenF() + color.blueF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5973 color.setRgbF( avg, avg, avg, alpha );
5974 }
5975
5976 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
5977}
5978
5979static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5980{
5981 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5982 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5983 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5984 if ( ratio > 1 )
5985 {
5986 ratio = 1;
5987 }
5988 else if ( ratio < 0 )
5989 {
5990 ratio = 0;
5991 }
5992
5993 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5994 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5995 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5996 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5997
5998 QColor newColor( red, green, blue, alpha );
5999
6000 return QgsSymbolLayerUtils::encodeColor( newColor );
6001}
6002
6003static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6004{
6005 const QVariant variant1 = values.at( 0 );
6006 const QVariant variant2 = values.at( 1 );
6007
6008 if ( variant1.userType() != variant2.userType() )
6009 {
6010 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6011 return QVariant();
6012 }
6013
6014 bool isQColor;
6015 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6016 if ( !color1.isValid() )
6017 return QVariant();
6018
6019 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6020 if ( !color2.isValid() )
6021 return QVariant();
6022
6023 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6024 {
6025 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6026 return QVariant();
6027 }
6028
6029 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6030
6031 // TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
6032 // NOLINTBEGIN(bugprone-narrowing-conversions)
6033
6034 QColor newColor;
6035 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6036 if ( color1.spec() == QColor::Spec::Cmyk )
6037 {
6038 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6039 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6040 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6041 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6042 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6043 }
6044 else
6045 {
6046 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6047 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6048 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6049 newColor = QColor::fromRgbF( red, green, blue, alpha );
6050 }
6051
6052 // NOLINTEND(bugprone-narrowing-conversions)
6053
6054 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6055}
6056
6057static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6058{
6059 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6060 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6061 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6062 QColor color = QColor( red, green, blue );
6063 if ( ! color.isValid() )
6064 {
6065 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6066 color = QColor( 0, 0, 0 );
6067 }
6068
6069 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6070}
6071
6072static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6073{
6074 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6075 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6076 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6077 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6078 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6079 if ( ! color.isValid() )
6080 {
6081 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6082 return QVariant();
6083 }
6084
6085 return color;
6086}
6087
6088static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6089{
6090 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6091 QVariant value = node->eval( parent, context );
6092 if ( parent->hasEvalError() )
6093 {
6094 parent->setEvalErrorString( QString() );
6095 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6097 value = node->eval( parent, context );
6099 }
6100 return value;
6101}
6102
6103static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6104{
6105 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6107 QVariant value = node->eval( parent, context );
6109 if ( value.toBool() )
6110 {
6111 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6113 value = node->eval( parent, context );
6115 }
6116 else
6117 {
6118 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6120 value = node->eval( parent, context );
6122 }
6123 return value;
6124}
6125
6126static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6127{
6128 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6129 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6130 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6131 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6132 QColor color = QColor( red, green, blue, alpha );
6133 if ( ! color.isValid() )
6134 {
6135 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6136 color = QColor( 0, 0, 0 );
6137 }
6138 return QgsSymbolLayerUtils::encodeColor( color );
6139}
6140
6141QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6142{
6143 QgsGradientColorRamp expRamp;
6144 const QgsColorRamp *ramp = nullptr;
6145 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6146 {
6147 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6148 ramp = &expRamp;
6149 }
6150 else
6151 {
6152 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6153 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6154 if ( ! ramp )
6156 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6157 return QVariant();
6158 }
6159 }
6160
6161 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6162 QColor color = ramp->color( value );
6163 return color;
6164}
6165
6166QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6167{
6168 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6169 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6170}
6171
6172static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6173{
6174 // Hue ranges from 0 - 360
6175 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6176 // Saturation ranges from 0 - 100
6177 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6178 // Lightness ranges from 0 - 100
6179 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6180
6181 QColor color = QColor::fromHslF( hue, saturation, lightness );
6182
6183 if ( ! color.isValid() )
6184 {
6185 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6186 color = QColor( 0, 0, 0 );
6187 }
6188
6189 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6190}
6191
6192static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6193{
6194 // Hue ranges from 0 - 360
6195 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6196 // Saturation ranges from 0 - 100
6197 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6198 // Lightness ranges from 0 - 100
6199 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6200 // Alpha ranges from 0 - 255
6201 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6202
6203 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6204 if ( ! color.isValid() )
6205 {
6206 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6207 color = QColor( 0, 0, 0 );
6208 }
6209 return QgsSymbolLayerUtils::encodeColor( color );
6210}
6211
6212static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6213{
6214 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6215 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6216 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6217 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6218
6219 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6220 if ( ! color.isValid() )
6221 {
6222 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6223 return QVariant();
6224 }
6225
6226 return color;
6227}
6228
6229static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6230{
6231 // Hue ranges from 0 - 360
6232 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6233 // Saturation ranges from 0 - 100
6234 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6235 // Value ranges from 0 - 100
6236 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6237
6238 QColor color = QColor::fromHsvF( hue, saturation, value );
6239
6240 if ( ! color.isValid() )
6241 {
6242 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6243 color = QColor( 0, 0, 0 );
6244 }
6245
6246 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6247}
6248
6249static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6250{
6251 // Hue ranges from 0 - 360
6252 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6253 // Saturation ranges from 0 - 100
6254 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6255 // Value ranges from 0 - 100
6256 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6257 // Alpha ranges from 0 - 255
6258 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6259
6260 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6261 if ( ! color.isValid() )
6262 {
6263 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6264 color = QColor( 0, 0, 0 );
6265 }
6266 return QgsSymbolLayerUtils::encodeColor( color );
6267}
6268
6269static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6270{
6271 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6272 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6273 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6274 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6275 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6276
6277 if ( ! color.isValid() )
6278 {
6279 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6280 return QVariant();
6281 }
6282
6283 return color;
6284}
6285
6286static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6287{
6288 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6289 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6290 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6291 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6292 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6293
6294 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6295 if ( ! color.isValid() )
6296 {
6297 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6298 return QVariant();
6299 }
6300
6301 return color;
6302}
6303
6304static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6305{
6306 // Cyan ranges from 0 - 100
6307 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6308 // Magenta ranges from 0 - 100
6309 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6310 // Yellow ranges from 0 - 100
6311 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6312 // Black ranges from 0 - 100
6313 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6314
6315 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6316
6317 if ( ! color.isValid() )
6318 {
6319 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6320 color = QColor( 0, 0, 0 );
6321 }
6322
6323 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6324}
6325
6326static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6327{
6328 // Cyan ranges from 0 - 100
6329 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6330 // Magenta ranges from 0 - 100
6331 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6332 // Yellow ranges from 0 - 100
6333 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6334 // Black ranges from 0 - 100
6335 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6336 // Alpha ranges from 0 - 255
6337 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6338
6339 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6340 if ( ! color.isValid() )
6341 {
6342 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6343 color = QColor( 0, 0, 0 );
6344 }
6345 return QgsSymbolLayerUtils::encodeColor( color );
6346}
6347
6348static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6349{
6350 const QVariant variant = values.at( 0 );
6351 bool isQColor;
6352 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6353 if ( !color.isValid() )
6354 return QVariant();
6355
6356 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6357 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6358 return color.red();
6359 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6360 return color.green();
6361 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6362 return color.blue();
6363 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6364 return color.alpha();
6365 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6366 return static_cast< double >( color.hsvHueF() * 360 );
6367 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6368 return static_cast< double >( color.hsvSaturationF() * 100 );
6369 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6370 return static_cast< double >( color.valueF() * 100 );
6371 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6372 return static_cast< double >( color.hslHueF() * 360 );
6373 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6374 return static_cast< double >( color.hslSaturationF() * 100 );
6375 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6376 return static_cast< double >( color.lightnessF() * 100 );
6377 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6378 return static_cast< double >( color.cyanF() * 100 );
6379 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6380 return static_cast< double >( color.magentaF() * 100 );
6381 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6382 return static_cast< double >( color.yellowF() * 100 );
6383 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6384 return static_cast< double >( color.blackF() * 100 );
6385
6386 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6387 return QVariant();
6388}
6389
6390static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6391{
6392 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6393 if ( map.empty() )
6394 {
6395 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6396 return QVariant();
6397 }
6398
6399 QList< QColor > colors;
6401 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6402 {
6403 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6404 if ( !colors.last().isValid() )
6405 {
6406 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6407 return QVariant();
6408 }
6409
6410 double step = it.key().toDouble();
6411 if ( it == map.constBegin() )
6412 {
6413 if ( step != 0.0 )
6414 stops << QgsGradientStop( step, colors.last() );
6415 }
6416 else if ( it == map.constEnd() )
6417 {
6418 if ( step != 1.0 )
6419 stops << QgsGradientStop( step, colors.last() );
6420 }
6421 else
6422 {
6423 stops << QgsGradientStop( step, colors.last() );
6424 }
6425 }
6426 bool discrete = values.at( 1 ).toBool();
6427
6428 if ( colors.empty() )
6429 return QVariant();
6430
6431 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6432}
6433
6434static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6435{
6436 const QVariant variant = values.at( 0 );
6437 bool isQColor;
6438 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6439 if ( !color.isValid() )
6440 return QVariant();
6441
6442 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6443 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6444 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6445 color.setRed( std::clamp( value, 0, 255 ) );
6446 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6447 color.setGreen( std::clamp( value, 0, 255 ) );
6448 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6449 color.setBlue( std::clamp( value, 0, 255 ) );
6450 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6451 color.setAlpha( std::clamp( value, 0, 255 ) );
6452 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6453 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6454 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6455 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6456 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6457 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6458 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6459 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6460 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6461 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6462 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6463 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6464 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6465 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6466 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6467 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6468 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6469 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6470 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6471 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6472 else
6473 {
6474 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6475 return QVariant();
6476 }
6477 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6478}
6479
6480static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6481{
6482 const QVariant variant = values.at( 0 );
6483 bool isQColor;
6484 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6485 if ( !color.isValid() )
6486 return QVariant();
6487
6488 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6489
6490 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6491}
6492
6493static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6494{
6495 const QVariant variant = values.at( 0 );
6496 bool isQColor;
6497 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6498 if ( !color.isValid() )
6499 return QVariant();
6500
6501 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6502
6503 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6504}
6505
6506static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6507{
6508 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6509 QgsGeometry geom = feat.geometry();
6510 if ( !geom.isNull() )
6511 return QVariant::fromValue( geom );
6512 return QVariant();
6513}
6514
6515static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6516{
6517 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6518 if ( !feat.isValid() )
6519 return QVariant();
6520 return feat.id();
6521}
6522
6523static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6524{
6525 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6526 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
6527 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6528
6529 if ( !sCrs.isValid() )
6530 return QVariant::fromValue( fGeom );
6531
6532 if ( !dCrs.isValid() )
6533 return QVariant::fromValue( fGeom );
6534
6536 if ( context )
6537 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6538 QgsCoordinateTransform t( sCrs, dCrs, tContext );
6539 try
6540 {
6542 return QVariant::fromValue( fGeom );
6543 }
6544 catch ( QgsCsException &cse )
6545 {
6546 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6547 return QVariant();
6548 }
6549 return QVariant();
6550}
6551
6552
6553static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6554{
6555 bool foundLayer = false;
6556 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6557
6558 //no layer found
6559 if ( !featureSource || !foundLayer )
6560 {
6561 return QVariant();
6562 }
6563
6564 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6565
6567 req.setFilterFid( fid );
6568 req.setTimeout( 10000 );
6569 req.setRequestMayBeNested( true );
6570 if ( context )
6571 req.setFeedback( context->feedback() );
6572 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6573
6574 QgsFeature fet;
6575 QVariant result;
6576 if ( fIt.nextFeature( fet ) )
6577 result = QVariant::fromValue( fet );
6578
6579 return result;
6580}
6581
6582static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6583{
6584 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6585 bool foundLayer = false;
6586 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6587
6588 //no layer found
6589 if ( !featureSource || !foundLayer )
6590 {
6591 return QVariant();
6592 }
6594 QString cacheValueKey;
6595 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6596 {
6597 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6598
6599 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6600 QString filterString;
6601 for ( ; i != attributeMap.constEnd(); ++i )
6602 {
6603 if ( !filterString.isEmpty() )
6604 {
6605 filterString.append( " AND " );
6606 }
6607 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6608 }
6609 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6610 if ( context && context->hasCachedValue( cacheValueKey ) )
6611 {
6612 return context->cachedValue( cacheValueKey );
6613 }
6614 req.setFilterExpression( filterString );
6615 }
6616 else
6617 {
6618 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6619 int attributeId = featureSource->fields().lookupField( attribute );
6620 if ( attributeId == -1 )
6621 {
6622 return QVariant();
6623 }
6624
6625 const QVariant &attVal = values.at( 2 );
6626
6627 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6628 if ( context && context->hasCachedValue( cacheValueKey ) )
6629 {
6630 return context->cachedValue( cacheValueKey );
6631 }
6632
6634 }
6635 req.setLimit( 1 );
6636 req.setTimeout( 10000 );
6637 req.setRequestMayBeNested( true );
6638 if ( context )
6639 req.setFeedback( context->feedback() );
6640 if ( !parent->needsGeometry() )
6641 {
6643 }
6644 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6645
6646 QgsFeature fet;
6647 QVariant res;
6648 if ( fIt.nextFeature( fet ) )
6649 {
6650 res = QVariant::fromValue( fet );
6651 }
6652
6653 if ( context )
6654 context->setCachedValue( cacheValueKey, res );
6655 return res;
6656}
6657
6658static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6659{
6660 QVariant result;
6661 QString fieldName;
6662
6663 if ( context )
6664 {
6665 if ( !values.isEmpty() )
6666 {
6667 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6668 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6669 fieldName = col->name();
6670 else if ( values.size() == 2 )
6671 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6672 }
6673
6674 QVariant value = values.at( 0 );
6675
6676 const QgsFields fields = context->fields();
6677 int fieldIndex = fields.lookupField( fieldName );
6678
6679 if ( fieldIndex == -1 )
6680 {
6681 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6682 }
6683 else
6684 {
6685 // TODO this function is NOT thread safe
6687 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6689
6690 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6691 if ( context->hasCachedValue( cacheValueKey ) )
6692 {
6693 return context->cachedValue( cacheValueKey );
6694 }
6695
6696 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6698
6699 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6700
6701 QVariant cache;
6702 if ( !context->hasCachedValue( cacheKey ) )
6703 {
6704 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6705 context->setCachedValue( cacheKey, cache );
6706 }
6707 else
6708 cache = context->cachedValue( cacheKey );
6709
6710 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6711
6712 context->setCachedValue( cacheValueKey, result );
6713 }
6714 }
6715 else
6716 {
6717 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6718 }
6719
6720 return result;
6721}
6722
6723static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6724{
6725 const QVariant data = values.at( 0 );
6726 const QMimeDatabase db;
6727 return db.mimeTypeForData( data.toByteArray() ).name();
6728}
6729
6730static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6731{
6732 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6733
6734 bool foundLayer = false;
6735 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6736 {
6737 if ( !layer )
6738 return QVariant();
6739
6740 // here, we always prefer the layer metadata values over the older server-specific published values
6741 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6742 return layer->name();
6743 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6744 return layer->id();
6745 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6746 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
6747 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6748 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
6749 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6750 {
6751 QStringList keywords;
6752 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6753 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6754 {
6755 keywords.append( it.value() );
6756 }
6757 if ( !keywords.isEmpty() )
6758 return keywords;
6759 return layer->serverProperties()->keywordList();
6760 }
6761 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6762 return layer->serverProperties()->dataUrl();
6763 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6764 {
6765 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
6766 }
6767 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6768 return layer->serverProperties()->attributionUrl();
6769 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6770 return layer->publicSource();
6771 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6772 return layer->minimumScale();
6773 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6774 return layer->maximumScale();
6775 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6776 return layer->isEditable();
6777 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6778 return layer->crs().authid();
6779 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6780 return layer->crs().toProj();
6781 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6782 return layer->crs().description();
6783 else if ( QString::compare( layerProperty, QStringLiteral( "crs_ellipsoid" ), Qt::CaseInsensitive ) == 0 )
6784 return layer->crs().ellipsoidAcronym();
6785 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6786 {
6787 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6788 QVariant result = QVariant::fromValue( extentGeom );
6789 return result;
6790 }
6791 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6792 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6793 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6794 {
6795 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6796 return decodedUri.value( QStringLiteral( "path" ) );
6797 }
6798 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6799 {
6800 switch ( layer->type() )
6801 {
6803 return QCoreApplication::translate( "expressions", "Vector" );
6805 return QCoreApplication::translate( "expressions", "Raster" );
6807 return QCoreApplication::translate( "expressions", "Mesh" );
6809 return QCoreApplication::translate( "expressions", "Vector Tile" );
6811 return QCoreApplication::translate( "expressions", "Plugin" );
6813 return QCoreApplication::translate( "expressions", "Annotation" );
6815 return QCoreApplication::translate( "expressions", "Point Cloud" );
6817 return QCoreApplication::translate( "expressions", "Group" );
6819 return QCoreApplication::translate( "expressions", "Tiled Scene" );
6820 }
6821 }
6822 else
6823 {
6824 //vector layer methods
6825 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6826 if ( vLayer )
6827 {
6828 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6829 return vLayer->storageType();
6830 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6832 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6833 return QVariant::fromValue( vLayer->featureCount() );
6834 }
6835 }
6836
6837 return QVariant();
6838 }, foundLayer );
6839
6840 if ( !foundLayer )
6841 return QVariant();
6842 else
6843 return res;
6844}
6845
6846static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6847{
6848 const QString uriPart = values.at( 1 ).toString();
6849
6850 bool foundLayer = false;
6851
6852 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
6853 {
6854 if ( !layer->dataProvider() )
6855 {
6856 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
6857 return QVariant();
6858 }
6859
6860 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
6861
6862 if ( !uriPart.isNull() )
6863 {
6864 return decodedUri.value( uriPart );
6865 }
6866 else
6867 {
6868 return decodedUri;
6869 }
6870 }, foundLayer );
6871
6872 if ( !foundLayer )
6873 {
6874 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
6875 return QVariant();
6876 }
6877 else
6878 {
6879 return res;
6880 }
6881}
6882
6883static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6884{
6885 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6886 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6887
6888 bool foundLayer = false;
6889 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
6890 {
6891 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6892 if ( !rl )
6893 return QVariant();
6894
6895 if ( band < 1 || band > rl->bandCount() )
6896 {
6897 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
6898 return QVariant();
6899 }
6900
6902
6903 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6905 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6907 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6909 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6911 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6913 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6915 else
6916 {
6917 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6918 return QVariant();
6919 }
6920
6921 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6922 switch ( stat )
6923 {
6925 return stats.mean;
6927 return stats.stdDev;
6929 return stats.minimumValue;
6931 return stats.maximumValue;
6933 return stats.range;
6935 return stats.sum;
6936 default:
6937 break;
6938 }
6939 return QVariant();
6940 }, foundLayer );
6941
6942 if ( !foundLayer )
6943 {
6944#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
6945 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
6946#endif
6947 return QVariant();
6948 }
6949 else
6950 {
6951 return res;
6952 }
6953}
6954
6955static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6956{
6957 return values;
6958}
6959
6960static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6961{
6962 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6963 bool ascending = values.value( 1 ).toBool();
6964 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6965 return list;
6966}
6967
6968static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6969{
6970 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6971}
6972
6973static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6974{
6975 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6976}
6977
6978static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6979{
6980 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6981}
6982
6983static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6984{
6985 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6986 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6987 int match = 0;
6988 for ( const auto &item : listB )
6989 {
6990 if ( listA.contains( item ) )
6991 match++;
6992 }
6993
6994 return QVariant( match == listB.count() );
6995}
6996
6997static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6998{
6999 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7000}
7001
7002static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7003{
7004 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7005 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7006 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
7007 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7008 return list.at( list.length() + pos );
7009 return QVariant();
7010}
7011
7012static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7013{
7014 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7015 return list.value( 0 );
7016}
7017
7018static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7019{
7020 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7021 return list.value( list.size() - 1 );
7022}
7023
7024static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7025{
7026 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7027 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7028}
7029
7030static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7031{
7032 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7033 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7034}
7035
7036static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7037{
7038 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7039 int i = 0;
7040 double total = 0.0;
7041 for ( const QVariant &item : list )
7042 {
7043 switch ( item.userType() )
7044 {
7045 case QMetaType::Int:
7046 case QMetaType::UInt:
7047 case QMetaType::LongLong:
7048 case QMetaType::ULongLong:
7049 case QMetaType::Float:
7050 case QMetaType::Double:
7051 total += item.toDouble();
7052 ++i;
7053 break;
7054 }
7055 }
7056 return i == 0 ? QVariant() : total / i;
7057}
7058
7059static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7060{
7061 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7062 QVariantList numbers;
7063 for ( const auto &item : list )
7064 {
7065 switch ( item.userType() )
7066 {
7067 case QMetaType::Int:
7068 case QMetaType::UInt:
7069 case QMetaType::LongLong:
7070 case QMetaType::ULongLong:
7071 case QMetaType::Float:
7072 case QMetaType::Double:
7073 numbers.append( item );
7074 break;
7075 }
7076 }
7077 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7078 const int count = numbers.count();
7079 if ( count == 0 )
7080 {
7081 return QVariant();
7082 }
7083 else if ( count % 2 )
7084 {
7085 return numbers.at( count / 2 );
7086 }
7087 else
7088 {
7089 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7090 }
7091}
7092
7093static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7094{
7095 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7096 int i = 0;
7097 double total = 0.0;
7098 for ( const QVariant &item : list )
7099 {
7100 switch ( item.userType() )
7101 {
7102 case QMetaType::Int:
7103 case QMetaType::UInt:
7104 case QMetaType::LongLong:
7105 case QMetaType::ULongLong:
7106 case QMetaType::Float:
7107 case QMetaType::Double:
7108 total += item.toDouble();
7109 ++i;
7110 break;
7111 }
7112 }
7113 return i == 0 ? QVariant() : total;
7114}
7115
7116static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7117{
7118 QVariant result = value;
7119 result.convert( static_cast<int>( type ) );
7120 return result;
7121}
7122
7123static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7124{
7125 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7126 QHash< QVariant, int > hash;
7127 for ( const auto &item : list )
7128 {
7129 ++hash[item];
7130 }
7131 const QList< int > occurrences = hash.values();
7132 if ( occurrences.empty() )
7133 return QVariantList();
7134
7135 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7136
7137 const QString option = values.at( 1 ).toString();
7138 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7139 {
7140 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7141 }
7142 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7143 {
7144 if ( hash.isEmpty() )
7145 return QVariant();
7146
7147 return QVariant( hash.key( maxValue ) );
7148 }
7149 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7150 {
7151 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7152 }
7153 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
7154 {
7155 if ( maxValue * 2 <= list.size() )
7156 return QVariant();
7157
7158 return QVariant( hash.key( maxValue ) );
7159 }
7160 else
7161 {
7162 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7163 return QVariant();
7164 }
7165}
7166
7167static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7168{
7169 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7170 QHash< QVariant, int > hash;
7171 for ( const auto &item : list )
7172 {
7173 ++hash[item];
7174 }
7175 const QList< int > occurrences = hash.values();
7176 if ( occurrences.empty() )
7177 return QVariantList();
7178
7179 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7180
7181 const QString option = values.at( 1 ).toString();
7182 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7183 {
7184 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7185 }
7186 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7187 {
7188 if ( hash.isEmpty() )
7189 return QVariant();
7190
7191 return QVariant( hash.key( minValue ) );
7192 }
7193 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7194 {
7195 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7196 }
7197 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
7198 {
7199 if ( hash.isEmpty() )
7200 return QVariant();
7201
7202 // Remove the majority, all others are minority
7203 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7204 if ( maxValue * 2 > list.size() )
7205 hash.remove( hash.key( maxValue ) );
7206
7207 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7208 }
7209 else
7210 {
7211 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7212 return QVariant();
7213 }
7214}
7215
7216static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7217{
7218 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7219 list.append( values.at( 1 ) );
7220 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7221}
7222
7223static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7224{
7225 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7226 list.prepend( values.at( 1 ) );
7227 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7228}
7229
7230static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7231{
7232 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7233 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7234 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7235}
7236
7237static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7238{
7239 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7240 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7241 if ( position < 0 )
7242 position = position + list.length();
7243 if ( position >= 0 && position < list.length() )
7244 list.removeAt( position );
7245 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7246}
7247
7248static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7249{
7250 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7251 return QVariant();
7252
7253 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7254
7255 const QVariant toRemove = values.at( 1 );
7256 if ( QgsVariantUtils::isNull( toRemove ) )
7257 {
7258 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7259 {
7260 return QgsVariantUtils::isNull( element );
7261 } ), list.end() );
7262 }
7263 else
7264 {
7265 list.removeAll( toRemove );
7266 }
7267 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7268}
7269
7270static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7271{
7272 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7273 {
7274 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7275
7276 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7277 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7278 {
7279 int index = list.indexOf( it.key() );
7280 while ( index >= 0 )
7281 {
7282 list.replace( index, it.value() );
7283 index = list.indexOf( it.key() );
7284 }
7285 }
7286
7287 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7288 }
7289 else if ( values.count() == 3 )
7290 {
7291 QVariantList before;
7292 QVariantList after;
7293 bool isSingleReplacement = false;
7294
7295 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7296 {
7297 before = QVariantList() << values.at( 1 );
7298 }
7299 else
7300 {
7301 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7302 }
7303
7304 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7305 {
7306 after = QVariantList() << values.at( 2 );
7307 isSingleReplacement = true;
7308 }
7309 else
7310 {
7311 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7312 }
7313
7314 if ( !isSingleReplacement && before.length() != after.length() )
7315 {
7316 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7317 return QVariant();
7318 }
7319
7320 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7321 for ( int i = 0; i < before.length(); i++ )
7322 {
7323 int index = list.indexOf( before.at( i ) );
7324 while ( index >= 0 )
7325 {
7326 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7327 index = list.indexOf( before.at( i ) );
7328 }
7329 }
7330
7331 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7332 }
7333 else
7334 {
7335 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7336 return QVariant();
7337 }
7338}
7339
7340static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7341{
7342 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7343 QVariantList list_new;
7344
7345 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7346 {
7347 while ( list.removeOne( cur ) )
7348 {
7349 list_new.append( cur );
7350 }
7351 }
7352
7353 list_new.append( list );
7354
7355 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7356}
7357
7358static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7359{
7360 QVariantList list;
7361 for ( const QVariant &cur : values )
7362 {
7363 list += QgsExpressionUtils::getListValue( cur, parent );
7364 }
7365 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7366}
7367
7368static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7369{
7370 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7371 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7372 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7373 int slice_length = 0;
7374 // negative positions means positions taken relative to the end of the array
7375 if ( start_pos < 0 )
7376 {
7377 start_pos = list.length() + start_pos;
7378 }
7379 if ( end_pos >= 0 )
7380 {
7381 slice_length = end_pos - start_pos + 1;
7382 }
7383 else
7384 {
7385 slice_length = list.length() + end_pos - start_pos + 1;
7386 }
7387 //avoid negative lengths in QList.mid function
7388 if ( slice_length < 0 )
7389 {
7390 slice_length = 0;
7391 }
7392 list = list.mid( start_pos, slice_length );
7393 return list;
7394}
7395
7396static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7397{
7398 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7399 std::reverse( list.begin(), list.end() );
7400 return list;
7401}
7402
7403static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7404{
7405 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7406 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7407 for ( const QVariant &cur : array2 )
7408 {
7409 if ( array1.contains( cur ) )
7410 return QVariant( true );
7411 }
7412 return QVariant( false );
7413}
7414
7415static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7416{
7417 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7418
7419 QVariantList distinct;
7420
7421 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7422 {
7423 if ( !distinct.contains( *it ) )
7424 {
7425 distinct += ( *it );
7426 }
7427 }
7428
7429 return distinct;
7430}
7431
7432static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7433{
7434 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7435 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7436 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7437
7438 QString str;
7439
7440 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7441 {
7442 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7443 if ( it != ( array.constEnd() - 1 ) )
7444 {
7445 str += delimiter;
7446 }
7447 }
7448
7449 return QVariant( str );
7450}
7451
7452static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7453{
7454 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7455 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7456 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7457
7458 QStringList list = str.split( delimiter );
7459 QVariantList array;
7460
7461 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7462 {
7463 array += ( !( *it ).isEmpty() ) ? *it : empty;
7464 }
7465
7466 return array;
7467}
7468
7469static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7470{
7471 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7472 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7473 if ( document.isNull() )
7474 return QVariant();
7475
7476 return document.toVariant();
7477}
7478
7479static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7480{
7481 Q_UNUSED( parent )
7482 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7483 return QString( document.toJson( QJsonDocument::Compact ) );
7484}
7485
7486static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7487{
7488 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7489 if ( str.isEmpty() )
7490 return QVariantMap();
7491 str = str.trimmed();
7492
7493 return QgsHstoreUtils::parse( str );
7494}
7495
7496static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7497{
7498 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7499 return QgsHstoreUtils::build( map );
7500}
7501
7502static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7503{
7504 QVariantMap result;
7505 for ( int i = 0; i + 1 < values.length(); i += 2 )
7506 {
7507 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7508 }
7509 return result;
7510}
7511
7512static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7513{
7514 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7515 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7516 QVariantMap resultMap;
7517
7518 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7519 {
7520 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7521 }
7522
7523 return resultMap;
7524}
7525
7526static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7527{
7528 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7529}
7530
7531static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7532{
7533 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7534}
7535
7536static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7537{
7538 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7539 map.remove( values.at( 1 ).toString() );
7540 return map;
7541}
7542
7543static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7544{
7545 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7546 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7547 return map;
7548}
7549
7550static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7551{
7552 QVariantMap result;
7553 for ( const QVariant &cur : values )
7554 {
7555 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7556 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7557 result.insert( it.key(), it.value() );
7558 }
7559 return result;
7560}
7561
7562static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7563{
7564 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7565}
7566
7567static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7568{
7569 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7570}
7571
7572static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7573{
7574 const QString envVarName = values.at( 0 ).toString();
7575 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7576 return QVariant();
7577
7578 return QProcessEnvironment::systemEnvironment().value( envVarName );
7579}
7580
7581static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7582{
7583 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7584 if ( parent->hasEvalError() )
7585 {
7586 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7587 return QVariant();
7588 }
7589 return QFileInfo( file ).completeBaseName();
7590}
7591
7592static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7593{
7594 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7595 if ( parent->hasEvalError() )
7596 {
7597 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7598 return QVariant();
7599 }
7600 return QFileInfo( file ).completeSuffix();
7601}
7602
7603static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7604{
7605 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7606 if ( parent->hasEvalError() )
7607 {
7608 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7609 return QVariant();
7610 }
7611 return QFileInfo::exists( file );
7612}
7613
7614static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7615{
7616 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7617 if ( parent->hasEvalError() )
7618 {
7619 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7620 return QVariant();
7621 }
7622 return QFileInfo( file ).fileName();
7623}
7624
7625static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7626{
7627 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7628 if ( parent->hasEvalError() )
7629 {
7630 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7631 return QVariant();
7632 }
7633 return QFileInfo( file ).isFile();
7634}
7635
7636static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7637{
7638 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7639 if ( parent->hasEvalError() )
7640 {
7641 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7642 return QVariant();
7643 }
7644 return QFileInfo( file ).isDir();
7645}
7646
7647static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7648{
7649 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7650 if ( parent->hasEvalError() )
7651 {
7652 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7653 return QVariant();
7654 }
7655 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7656}
7657
7658static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7659{
7660 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7661 if ( parent->hasEvalError() )
7662 {
7663 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7664 return QVariant();
7665 }
7666 return QFileInfo( file ).size();
7667}
7668
7669static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7670{
7671 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7672}
7673
7674static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7675{
7676 QVariant hash;
7677 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7678 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7679
7680 if ( method == QLatin1String( "md4" ) )
7681 {
7682 hash = fcnHash( str, QCryptographicHash::Md4 );
7683 }
7684 else if ( method == QLatin1String( "md5" ) )
7685 {
7686 hash = fcnHash( str, QCryptographicHash::Md5 );
7687 }
7688 else if ( method == QLatin1String( "sha1" ) )
7689 {
7690 hash = fcnHash( str, QCryptographicHash::Sha1 );
7691 }
7692 else if ( method == QLatin1String( "sha224" ) )
7693 {
7694 hash = fcnHash( str, QCryptographicHash::Sha224 );
7695 }
7696 else if ( method == QLatin1String( "sha256" ) )
7697 {
7698 hash = fcnHash( str, QCryptographicHash::Sha256 );
7699 }
7700 else if ( method == QLatin1String( "sha384" ) )
7701 {
7702 hash = fcnHash( str, QCryptographicHash::Sha384 );
7703 }
7704 else if ( method == QLatin1String( "sha512" ) )
7705 {
7706 hash = fcnHash( str, QCryptographicHash::Sha512 );
7707 }
7708 else if ( method == QLatin1String( "sha3_224" ) )
7709 {
7710 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7711 }
7712 else if ( method == QLatin1String( "sha3_256" ) )
7713 {
7714 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7715 }
7716 else if ( method == QLatin1String( "sha3_384" ) )
7717 {
7718 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7719 }
7720 else if ( method == QLatin1String( "sha3_512" ) )
7721 {
7722 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7723 }
7724 else if ( method == QLatin1String( "keccak_224" ) )
7725 {
7726 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7727 }
7728 else if ( method == QLatin1String( "keccak_256" ) )
7729 {
7730 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7731 }
7732 else if ( method == QLatin1String( "keccak_384" ) )
7733 {
7734 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7735 }
7736 else if ( method == QLatin1String( "keccak_512" ) )
7737 {
7738 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7739 }
7740 else
7741 {
7742 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7743 }
7744 return hash;
7745}
7746
7747static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7748{
7749 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7750}
7751
7752static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7753{
7754 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7755}
7756
7757static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7758{
7759 const QByteArray input = values.at( 0 ).toByteArray();
7760 return QVariant( QString( input.toBase64() ) );
7761}
7762
7763static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7764{
7765 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7766 QUrlQuery query;
7767 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7768 {
7769 query.addQueryItem( it.key(), it.value().toString() );
7770 }
7771 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7772}
7773
7774static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7775{
7776 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7777 const QByteArray base64 = value.toLocal8Bit();
7778 const QByteArray decoded = QByteArray::fromBase64( base64 );
7779 return QVariant( decoded );
7780}
7781
7782typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7783
7784static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const RelationFunction &relationFunction, bool invert = false, double bboxGrow = 0, bool isNearestFunc = false, bool isIntersectsFunc = false )
7785{
7786
7787 if ( ! context )
7788 {
7789 parent->setEvalErrorString( QStringLiteral( "This function was called without an expression context." ) );
7790 return QVariant();
7791 }
7792
7793 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7794 // TODO this function is NOT thread safe
7796 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7798
7799 QgsFeatureRequest request;
7800 request.setTimeout( 10000 );
7801 request.setRequestMayBeNested( true );
7802 request.setFeedback( context->feedback() );
7803
7804 // First parameter is the overlay layer
7805 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7807
7808 const bool layerCanBeCached = node->isStatic( parent, context );
7809 QVariant targetLayerValue = node->eval( parent, context );
7811
7812 // Second parameter is the expression to evaluate (or null for testonly)
7813 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7815 QString subExpString = node->dump();
7816
7817 bool testOnly = ( subExpString == "NULL" );
7818 // TODO this function is NOT thread safe
7820 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7822 if ( !targetLayer ) // No layer, no joy
7823 {
7824 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7825 return QVariant();
7826 }
7827
7828 // Third parameter is the filtering expression
7829 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7831 QString filterString = node->dump();
7832 if ( filterString != "NULL" )
7833 {
7834 request.setFilterExpression( filterString ); //filter cached features
7835 }
7836
7837 // Fourth parameter is the limit
7838 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7840 QVariant limitValue = node->eval( parent, context );
7842 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7843
7844 // Fifth parameter (for nearest only) is the max distance
7845 double max_distance = 0;
7846 if ( isNearestFunc ) //maxdistance param handling
7847 {
7848 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7850 QVariant distanceValue = node->eval( parent, context );
7852 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
7853 }
7854
7855 // Fifth or sixth (for nearest only) parameter is the cache toggle
7856 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
7858 QVariant cacheValue = node->eval( parent, context );
7860 bool cacheEnabled = cacheValue.toBool();
7861
7862 // Sixth parameter (for intersects only) is the min overlap (area or length)
7863 // Seventh parameter (for intersects only) is the min inscribed circle radius
7864 // Eighth parameter (for intersects only) is the return_details
7865 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
7866 double minOverlap { -1 };
7867 double minInscribedCircleRadius { -1 };
7868 bool returnDetails = false; //#spellok
7869 bool sortByMeasure = false;
7870 bool sortAscending = false;
7871 bool requireMeasures = false;
7872 bool overlapOrRadiusFilter = false;
7873 if ( isIntersectsFunc )
7874 {
7875
7876 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7878 const QVariant minOverlapValue = node->eval( parent, context );
7880 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
7881 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7883 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
7885 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
7886 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
7887 // Return measures is only effective when an expression is set
7888 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
7889 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
7890 // Sort by measures is only effective when an expression is set
7891 const QString sorting { node->eval( parent, context ).toString().toLower() };
7892 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
7893 sortAscending = sorting.startsWith( "asc" );
7894 requireMeasures = sortByMeasure || returnDetails; //#spellok
7895 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
7896 }
7897
7898
7899 FEAT_FROM_CONTEXT( context, feat )
7900 const QgsGeometry geometry = feat.geometry();
7901
7902 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7903 {
7904 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7905 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7906 }
7907
7908 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7909
7910 QgsRectangle intDomain = geometry.boundingBox();
7911 if ( bboxGrow != 0 )
7912 {
7913 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7914 }
7915
7916 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7917
7918 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7919 // Otherwise, it can be toggled by the user
7920 QgsSpatialIndex spatialIndex;
7921 QgsVectorLayer *cachedTarget;
7922 QList<QgsFeature> features;
7923 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7924 {
7925 // If the cache (local spatial index) is enabled, we materialize the whole
7926 // layer, then do the request on that layer instead.
7927 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7928 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7929
7930 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7931 {
7932 cachedTarget = targetLayer->materialize( request );
7933 if ( layerCanBeCached )
7934 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7935 }
7936 else
7937 {
7938 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7939 }
7940
7941 if ( !context->hasCachedValue( cacheIndex ) )
7942 {
7943 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7944 if ( layerCanBeCached )
7945 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7946 }
7947 else
7948 {
7949 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7950 }
7951
7952 QList<QgsFeatureId> fidsList;
7953 if ( isNearestFunc )
7954 {
7955 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7956 }
7957 else
7958 {
7959 fidsList = spatialIndex.intersects( intDomain );
7960 }
7961
7962 QListIterator<QgsFeatureId> i( fidsList );
7963 while ( i.hasNext() )
7964 {
7965 QgsFeatureId fId2 = i.next();
7966 if ( sameLayers && feat.id() == fId2 )
7967 continue;
7968 features.append( cachedTarget->getFeature( fId2 ) );
7969 }
7970
7971 }
7972 else
7973 {
7974 // If the cache (local spatial index) is not enabled, we directly
7975 // get the features from the target layer
7976 request.setFilterRect( intDomain );
7977 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7978 QgsFeature feat2;
7979 while ( fit.nextFeature( feat2 ) )
7980 {
7981 if ( sameLayers && feat.id() == feat2.id() )
7982 continue;
7983 features.append( feat2 );
7984 }
7985 }
7986
7987 QgsExpression subExpression;
7988 QgsExpressionContext subContext;
7989 if ( !testOnly )
7990 {
7991 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7992 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7993
7994 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7995 {
7996 subExpression = QgsExpression( subExpString );
7998 subExpression.prepare( &subContext );
7999 }
8000 else
8001 {
8002 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8003 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8004 }
8005 }
8006
8007 // //////////////////////////////////////////////////////////////////
8008 // Helper functions for geometry tests
8009
8010 // Test function for linestring geometries, returns TRUE if test passes
8011 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
8012 {
8013 bool testResult { false };
8014 // For return measures:
8015 QVector<double> overlapValues;
8016 const QgsGeometry merged { intersection.mergeLines() };
8017 for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
8018 {
8019 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
8020 // Check min overlap for intersection (if set)
8021 if ( minOverlap != -1 || requireMeasures )
8022 {
8023 overlapValue = geom->length();
8024 overlapValues.append( overlapValue );
8025 if ( minOverlap != -1 )
8026 {
8027 if ( overlapValue >= minOverlap )
8028 {
8029 testResult = true;
8030 }
8031 else
8032 {
8033 continue;
8034 }
8035 }
8036 }
8037 }
8038
8039 if ( ! overlapValues.isEmpty() )
8040 {
8041 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8042 }
8043
8044 return testResult;
8045 };
8046
8047 // Test function for polygon geometries, returns TRUE if test passes
8048 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
8049 {
8050 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8051 bool testResult { false };
8052 // For return measures:
8053 QVector<double> overlapValues;
8054 QVector<double> radiusValues;
8055 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8056 {
8057 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
8058 // Check min overlap for intersection (if set)
8059 if ( minOverlap != -1 || requireMeasures )
8060 {
8061 overlapValue = geom->area();
8062 overlapValues.append( geom->area() );
8063 if ( minOverlap != - 1 )
8064 {
8065 if ( overlapValue >= minOverlap )
8066 {
8067 testResult = true;
8068 }
8069 else
8070 {
8071 continue;
8072 }
8073 }
8074 }
8075
8076 // Check min inscribed circle radius for intersection (if set)
8077 if ( minInscribedCircleRadius != -1 || requireMeasures )
8078 {
8079 const QgsRectangle bbox = geom->boundingBox();
8080 const double width = bbox.width();
8081 const double height = bbox.height();
8082 const double size = width > height ? width : height;
8083 const double tolerance = size / 100.0;
8084 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8085 testResult = radiusValue >= minInscribedCircleRadius;
8086 radiusValues.append( radiusValues );
8087 }
8088 } // end for parts
8089
8090 // Get the max values
8091 if ( !radiusValues.isEmpty() )
8092 {
8093 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8094 }
8095
8096 if ( ! overlapValues.isEmpty() )
8097 {
8098 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8099 }
8100
8101 return testResult;
8102
8103 };
8104
8105
8106 bool found = false;
8107 int foundCount = 0;
8108 QVariantList results;
8109
8110 QListIterator<QgsFeature> i( features );
8111 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8112 {
8113
8114 QgsFeature feat2 = i.next();
8115
8116
8117 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8118 {
8119
8120 double overlapValue = -1;
8121 double radiusValue = -1;
8122
8123 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8124 {
8125
8126 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8127
8128 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8129 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8130 {
8131 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8132 intersection = QgsGeometry();
8133 QgsMultiPolygonXY poly;
8134 QgsMultiPolylineXY line;
8135 QgsMultiPointXY point;
8136 for ( const auto &geom : std::as_const( geometries ) )
8137 {
8138 switch ( geom.type() )
8139 {
8141 {
8142 poly.append( geom.asPolygon() );
8143 break;
8144 }
8146 {
8147 line.append( geom.asPolyline() );
8148 break;
8149 }
8151 {
8152 point.append( geom.asPoint() );
8153 break;
8154 }
8157 {
8158 break;
8159 }
8160 }
8161 }
8162
8163 switch ( geometry.type() )
8164 {
8166 {
8167 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8168 break;
8169 }
8171 {
8172 intersection = QgsGeometry::fromMultiPolylineXY( line );
8173 break;
8174 }
8176 {
8177 intersection = QgsGeometry::fromMultiPointXY( point );
8178 break;
8179 }
8182 {
8183 break;
8184 }
8185 }
8186 }
8187
8188 // Depending on the intersection geometry type and on the geometry type of
8189 // the tested geometry we can run different tests and collect different measures
8190 // that can be used for sorting (if required).
8191 switch ( intersection.type() )
8192 {
8193
8195 {
8196
8197 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8198 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8199
8200 if ( ! testResult && overlapOrRadiusFilter )
8201 {
8202 continue;
8203 }
8204
8205 break;
8206 }
8207
8209 {
8210
8211 // If the intersection is a linestring and a minimum circle is required
8212 // we can discard this result immediately.
8213 if ( minInscribedCircleRadius != -1 )
8214 {
8215 continue;
8216 }
8217
8218 // Otherwise a test for the overlap value is performed.
8219 const bool testResult { testLinestring( intersection, overlapValue ) };
8220
8221 if ( ! testResult && overlapOrRadiusFilter )
8222 {
8223 continue;
8224 }
8225
8226 break;
8227 }
8228
8230 {
8231
8232 // If the intersection is a point and a minimum circle is required
8233 // we can discard this result immediately.
8234 if ( minInscribedCircleRadius != -1 )
8235 {
8236 continue;
8237 }
8238
8239 bool testResult { false };
8240 if ( minOverlap != -1 || requireMeasures )
8241 {
8242 // Initially set this to 0 because it's a point intersection...
8243 overlapValue = 0;
8244 // ... but if the target geometry is not a point and the source
8245 // geometry is a point, we must record the length or the area
8246 // of the intersected geometry and use that as a measure for
8247 // sorting or reporting.
8248 if ( geometry.type() == Qgis::GeometryType::Point )
8249 {
8250 switch ( feat2.geometry().type() )
8251 {
8255 {
8256 break;
8257 }
8259 {
8260 testResult = testLinestring( feat2.geometry(), overlapValue );
8261 break;
8262 }
8264 {
8265 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8266 break;
8267 }
8268 }
8269 }
8270
8271 if ( ! testResult && overlapOrRadiusFilter )
8272 {
8273 continue;
8274 }
8275
8276 }
8277 break;
8278 }
8279
8282 {
8283 continue;
8284 }
8285 }
8286 }
8287
8288 found = true;
8289 foundCount++;
8290
8291 // We just want a single boolean result if there is any intersect: finish and return true
8292 if ( testOnly )
8293 break;
8294
8295 if ( !invert )
8296 {
8297 // We want a list of attributes / geometries / other expression values, evaluate now
8298 subContext.setFeature( feat2 );
8299 const QVariant expResult = subExpression.evaluate( &subContext );
8300
8301 if ( requireMeasures )
8302 {
8303 QVariantMap resultRecord;
8304 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
8305 resultRecord.insert( QStringLiteral( "result" ), expResult );
8306 // Overlap is always added because return measures was set
8307 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
8308 // Radius is only added when is different than -1 (because for linestrings is not set)
8309 if ( radiusValue != -1 )
8310 {
8311 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
8312 }
8313 results.append( resultRecord );
8314 }
8315 else
8316 {
8317 results.append( expResult );
8318 }
8319 }
8320 else
8321 {
8322 // If not, results is a list of found ids, which we'll inverse and evaluate below
8323 results.append( feat2.id() );
8324 }
8325 }
8326 }
8327
8328 if ( testOnly )
8329 {
8330 if ( invert )
8331 found = !found;//for disjoint condition
8332 return found;
8333 }
8334
8335 if ( !invert )
8336 {
8337 if ( requireMeasures )
8338 {
8339 if ( sortByMeasure )
8340 {
8341 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8342 {
8343 return sortAscending ?
8344 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
8345 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
8346 } );
8347 }
8348 // Resize
8349 if ( limit > 0 && results.size() > limit )
8350 {
8351 results.erase( results.begin() + limit );
8352 }
8353
8354 if ( ! returnDetails ) //#spellok
8355 {
8356 QVariantList expResults;
8357 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8358 {
8359 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
8360 }
8361 return expResults;
8362 }
8363 }
8364
8365 return results;
8366 }
8367
8368 // for disjoint condition returns the results for cached layers not intersected feats
8369 QVariantList disjoint_results;
8370 QgsFeature feat2;
8371 QgsFeatureRequest request2;
8372 request2.setLimit( limit );
8373 if ( context )
8374 request2.setFeedback( context->feedback() );
8375 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8376 while ( fi.nextFeature( feat2 ) )
8377 {
8378 if ( !results.contains( feat2.id() ) )
8379 {
8380 subContext.setFeature( feat2 );
8381 disjoint_results.append( subExpression.evaluate( &subContext ) );
8382 }
8383 }
8384 return disjoint_results;
8385
8386}
8387
8388// Intersect functions:
8389
8390static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8391{
8392 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8393}
8394
8395static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8396{
8397 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8398}
8399
8400static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8401{
8402 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8403}
8404
8405static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8406{
8407 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8408}
8409
8410static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8411{
8412 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8413}
8414
8415static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8416{
8417 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8418}
8420static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8421{
8422 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8423}
8424
8425static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8426{
8427 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8428}
8429
8430const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8431{
8432 // The construction of the list isn't thread-safe, and without the mutex,
8433 // crashes in the WFS provider may occur, since it can parse expressions
8434 // in parallel.
8435 // The mutex needs to be recursive.
8436 QMutexLocker locker( &sFunctionsMutex );
8437
8438 QList<QgsExpressionFunction *> &functions = *sFunctions();
8439
8440 if ( functions.isEmpty() )
8441 {
8443 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8444 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
8445 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
8446
8447 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8448 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8449 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8450
8451 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8452 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8453
8454 functions
8455 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
8456 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
8457 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
8458 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
8459 << new QgsStaticExpressionFunction( QStringLiteral( "bearing" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "source_crs" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "ellipsoid" ), true, QVariant() ), fcnBearing, QStringLiteral( "GeometryGroup" ) )
8460 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
8461 << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI_2 ), fcnProject, QStringLiteral( "GeometryGroup" ) )
8462 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
8463 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
8464 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
8465 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
8466 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
8467 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
8468 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
8469 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
8470 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
8471 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
8472 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
8473 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
8474 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
8475
8476 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
8477 randFunc->setIsStatic( false );
8478 functions << randFunc;
8479
8480 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRndF, QStringLiteral( "Math" ) );
8481 randfFunc->setIsStatic( false );
8482 functions << randfFunc;
8483
8484 functions
8485 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8486 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8487 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
8488 << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ), fcnLinearScale, QStringLiteral( "Math" ) )
8489 << new QgsStaticExpressionFunction( QStringLiteral( "scale_polynomial" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnPolynomialScale, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "scale_exp" ) )
8490 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exponential" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnExponentialScale, QStringLiteral( "Math" ) )
8491 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8492 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8493 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8494 << new QgsStaticExpressionFunction( QStringLiteral( "to_bool" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToBool, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tobool" ), /* handlesNull = */ true )
8495 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8496 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8497 << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tostring" ) )
8498 << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todatetime" ) )
8499 << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todate" ) )
8500 << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "totime" ) )
8501 << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tointerval" ) )
8502 << new QgsStaticExpressionFunction( QStringLiteral( "to_dm" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinute, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todm" ) )
8503 << new QgsStaticExpressionFunction( QStringLiteral( "to_dms" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinuteSecond, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todms" ) )
8504 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8505 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8506 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8507 << new QgsStaticExpressionFunction( QStringLiteral( "if" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "condition" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_true" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_false" ) ), fcnIf, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8508 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8509
8510 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8512 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8513 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8514 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8515 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8516 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8517 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8518 fcnAggregate,
8519 QStringLiteral( "Aggregates" ),
8520 QString(),
8521 []( const QgsExpressionNodeFunction * node )
8522 {
8523 // usesGeometry callback: return true if @parent variable is referenced
8524
8525 if ( !node )
8526 return true;
8527
8528 if ( !node->args() )
8529 return false;
8530
8531 QSet<QString> referencedVars;
8532 if ( node->args()->count() > 2 )
8533 {
8534 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8535 referencedVars = subExpressionNode->referencedVariables();
8536 }
8537
8538 if ( node->args()->count() > 3 )
8539 {
8540 QgsExpressionNode *filterNode = node->args()->at( 3 );
8541 referencedVars.unite( filterNode->referencedVariables() );
8542 }
8543 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8544 },
8545 []( const QgsExpressionNodeFunction * node )
8546 {
8547 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8548
8549 if ( !node )
8550 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8551
8552 if ( !node->args() )
8553 return QSet<QString>();
8554
8555 QSet<QString> referencedCols;
8556 QSet<QString> referencedVars;
8557
8558 if ( node->args()->count() > 2 )
8559 {
8560 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8561 referencedVars = subExpressionNode->referencedVariables();
8562 referencedCols = subExpressionNode->referencedColumns();
8563 }
8564 if ( node->args()->count() > 3 )
8565 {
8566 QgsExpressionNode *filterNode = node->args()->at( 3 );
8567 referencedVars = filterNode->referencedVariables();
8568 referencedCols.unite( filterNode->referencedColumns() );
8569 }
8570
8571 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8572 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8573 else
8574 return referencedCols;
8575 },
8576 true
8577 )
8578
8579 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8580 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8581 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8582 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8583 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8584 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8585 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8586
8587 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8588 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8589 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8590 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8591 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8592 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8593 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8594 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8595 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8596 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8597 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8598 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8599 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8600 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8601 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8602 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8603 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8604 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8605 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8606 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8607 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8608
8609 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8610 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8611
8612 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8613 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8614 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8615 fcnAge, QStringLiteral( "Date and Time" ) )
8616 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8617 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8618 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8619 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8620 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8621 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8622 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8623 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8624 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8625 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8626 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8627 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8628 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8629 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8630 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8631 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8632 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8633 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8634 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8635 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8636 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8637 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8638 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8639 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8640 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8641 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8642 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8643 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8644 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8645 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8646 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8647 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8648 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8649 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8650 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8651 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8652 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8653 << new QgsStaticExpressionFunction( QStringLiteral( "ltrim" ), QgsExpressionFunction::ParameterList()
8654 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8655 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnLTrim, QStringLiteral( "String" ) )
8656 << new QgsStaticExpressionFunction( QStringLiteral( "rtrim" ), QgsExpressionFunction::ParameterList()
8657 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8658 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnRTrim, QStringLiteral( "String" ) )
8659 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8660 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8661 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8662 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8663 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8664 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8665 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8666 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8667 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8668 << new QgsStaticExpressionFunction( QStringLiteral( "repeat" ), { QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )}, fcnRepeat, QStringLiteral( "String" ) )
8669 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8670 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8671 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8672 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8673 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8674 false, QSet< QString >(), false, QStringList(), true )
8675 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8676 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8677 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8678 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8679 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8680 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8681 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8682 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8683 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8684 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8685 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8686 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8687 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8688 << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
8689 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8690 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8691 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8692 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8693 fcnColorMixRgb, QStringLiteral( "Color" ) )
8694 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8695 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8696 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8697 fcnColorMix, QStringLiteral( "Color" ) )
8698 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8699 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8700 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8701 fcnColorRgb, QStringLiteral( "Color" ) )
8702 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgbf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8703 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8704 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8705 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8706 fcnColorRgbF, QStringLiteral( "Color" ) )
8707 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8708 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8709 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8710 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8711 fncColorRgba, QStringLiteral( "Color" ) )
8712 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8713 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8714 fcnRampColor, QStringLiteral( "Color" ) )
8715 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color_object" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8716 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8717 fcnRampColorObject, QStringLiteral( "Color" ) )
8718 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8719 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8720 fcnCreateRamp, QStringLiteral( "Color" ) )
8721 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8722 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8723 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8724 fcnColorHsl, QStringLiteral( "Color" ) )
8725 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8726 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8727 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8728 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8729 fncColorHsla, QStringLiteral( "Color" ) )
8730 << new QgsStaticExpressionFunction( QStringLiteral( "color_hslf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8731 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8732 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8733 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8734 fcnColorHslF, QStringLiteral( "Color" ) )
8735 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8736 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8737 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8738 fcnColorHsv, QStringLiteral( "Color" ) )
8739 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8740 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8741 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8742 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8743 fncColorHsva, QStringLiteral( "Color" ) )
8744 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsvf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8745 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8746 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8747 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8748 fcnColorHsvF, QStringLiteral( "Color" ) )
8749 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8750 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8751 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8752 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8753 fcnColorCmyk, QStringLiteral( "Color" ) )
8754 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8755 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8756 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8757 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8758 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8759 fncColorCmyka, QStringLiteral( "Color" ) )
8760 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmykf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8761 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8762 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8763 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8764 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8765 fcnColorCmykF, QStringLiteral( "Color" ) )
8766 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8767 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8768 fncColorPart, QStringLiteral( "Color" ) )
8769 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8770 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8771 fncDarker, QStringLiteral( "Color" ) )
8772 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8773 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8774 fncLighter, QStringLiteral( "Color" ) )
8775 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8776
8777 // file info
8778 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8779 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8780 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8781 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8782 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8783 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8784 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8785 fcnFileName, QStringLiteral( "Files and Paths" ) )
8786 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8787 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8788 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8789 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8790 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8791 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8792 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8793 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8794
8795 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8796 fcnExif, QStringLiteral( "Files and Paths" ) )
8797 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8798 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8799
8800 // hash
8801 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8802 fcnGenericHash, QStringLiteral( "Conversions" ) )
8803 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8804 fcnHashMd5, QStringLiteral( "Conversions" ) )
8805 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8806 fcnHashSha256, QStringLiteral( "Conversions" ) )
8807
8808 //base64
8809 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8810 fcnToBase64, QStringLiteral( "Conversions" ) )
8811 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8812 fcnFromBase64, QStringLiteral( "Conversions" ) )
8813
8814 // deprecated stuff - hidden from users
8815 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8816
8817 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8818 geomFunc->setIsStatic( false );
8819 functions << geomFunc;
8820
8821 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8822 areaFunc->setIsStatic( false );
8823 functions << areaFunc;
8824
8825 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8826
8827 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8828 lengthFunc->setIsStatic( false );
8829 functions << lengthFunc;
8830
8831 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8832 perimeterFunc->setIsStatic( false );
8833 functions << perimeterFunc;
8834
8835 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8836
8837 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8839 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
8840
8841 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
8842 xFunc->setIsStatic( false );
8843 functions << xFunc;
8844
8845 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
8846 yFunc->setIsStatic( false );
8847 functions << yFunc;
8848
8849 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
8850 zFunc->setIsStatic( false );
8851 functions << zFunc;
8852
8853 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
8854 {
8855 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
8856 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
8857 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
8858 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
8859 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
8860 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
8861 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
8862 };
8863 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
8864 while ( i.hasNext() )
8865 {
8866 i.next();
8868 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8869 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8870 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8871 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
8872 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
8873 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
8874 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
8875 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
8876 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
8877 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8878
8879 // The current feature is accessed for the geometry, so this should not be cached
8880 fcnGeomOverlayFunc->setIsStatic( false );
8881 functions << fcnGeomOverlayFunc;
8882 }
8883
8884 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
8885 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8886 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8887 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8888 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
8889 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
8890 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
8891 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8892 // The current feature is accessed for the geometry, so this should not be cached
8893 fcnGeomOverlayNearestFunc->setIsStatic( false );
8894 functions << fcnGeomOverlayNearestFunc;
8895
8896 functions
8897 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
8898 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
8899 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
8900 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
8901 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
8902 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
8903 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
8904 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
8905 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8906 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
8907 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
8908 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
8909 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
8910 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
8911 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8912 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
8913 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
8914 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
8915 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
8916 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
8917 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8918 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8919 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
8920 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
8921 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
8922 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8923 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8924 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8925 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
8926 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
8927 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8928 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
8929 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
8930 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8931 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8932 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
8933 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
8934 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8935 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8936 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
8937 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
8938 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
8939 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
8940 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8941 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
8942 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
8943 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
8944 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8945 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8946 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
8947 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
8948 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
8949 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
8950 {
8951 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8952#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
8953 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
8954#else
8955 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
8956#endif
8957 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
8958 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
8959
8960 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
8961 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
8962 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
8963 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
8964
8965 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
8966 xAtFunc->setIsStatic( false );
8967 functions << xAtFunc;
8968
8969
8970 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
8971 yAtFunc->setIsStatic( false );
8972 functions << yAtFunc;
8973
8974 functions
8975 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
8976 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
8977 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
8978 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
8979 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
8980 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
8981 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8982 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
8983 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
8984 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
8985 << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ), fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "bbox" ) )
8986 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8987 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8988 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
8989 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8990 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8991 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
8992 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8993 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8994 fcnTouches, QStringLiteral( "GeometryGroup" ) )
8995 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8996 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8997 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
8998 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8999 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9000 fcnContains, QStringLiteral( "GeometryGroup" ) )
9001 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9002 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9003 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
9004 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9005 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9006 fcnWithin, QStringLiteral( "GeometryGroup" ) )
9007 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9008 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
9009 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
9010 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
9011 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9012 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
9013 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
9014 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
9015 fcnRotate, QStringLiteral( "GeometryGroup" ) )
9016 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9017 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
9018 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
9019 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
9020 fcnScale, QStringLiteral( "GeometryGroup" ) )
9021 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9022 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
9023 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
9024 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
9025 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
9026 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
9027 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
9028 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
9029 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
9030 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
9031 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
9032 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9033 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9034 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
9035 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
9036 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
9037 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
9038 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
9039 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9040 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
9041 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9042 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
9043 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9044 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
9045 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9046 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
9047 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
9048 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
9049 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
9050 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9051 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
9052 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
9053 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9054 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
9055 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9056 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9057 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
9058 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9059 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9060 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9061 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9062 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9063 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
9064 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9065 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9066 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9067 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9068 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9069 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
9070 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9071 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
9072 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
9073 fcnExtend, QStringLiteral( "GeometryGroup" ) )
9074 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
9075 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
9076 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9077 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
9078 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, { QStringLiteral( "String" ), QStringLiteral( "GeometryGroup" ) } )
9079 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
9080 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9081 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9082 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
9083 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9084 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9085 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
9086 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
9087 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
9088 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
9089 {
9090 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
9091 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9092 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
9093 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
9094 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
9095 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
9096 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
9097 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
9098 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
9099 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
9100 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
9101 {
9102 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9103 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9104 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9105 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9106 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
9107 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
9108 {
9109 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9110 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9111 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9112 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9113 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9114 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9115 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9116 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
9117 {
9118 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9119 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9120 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9121 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9122 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
9123 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
9124 {
9125 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9126 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9127 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9128 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9129 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9130 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9131 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9132 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
9133 {
9134 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9135 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9136 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9137 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9138 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
9139 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
9140 {
9141 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9142 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9143 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9144 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9145 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9146 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9147 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9148 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
9149 {
9150 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9151 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
9152 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
9153 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
9154 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
9155 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
9156 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
9157 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
9158 {
9159 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9160 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
9161 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
9162 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
9163 {
9164 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9165 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9166 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
9167 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
9168 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
9169 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
9170 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
9171 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
9172 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
9173 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
9174 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
9175 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
9176 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9177 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
9178#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9179 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9180 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
9181 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
9182#endif
9183 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
9184 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9185 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
9186 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
9187 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9188 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
9189 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
9190 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9191 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9192 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
9193 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9194 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9195 fcnDifference, QStringLiteral( "GeometryGroup" ) )
9196 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9197 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9198 fcnDistance, QStringLiteral( "GeometryGroup" ) )
9199 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9200 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
9201 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
9202 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9203 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9204 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
9205 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9206 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9207 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
9208 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9209 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9210 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9211 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9212 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9213 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9214 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9215 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
9216 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
9217 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9218 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9219 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
9220 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9221 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
9222 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
9223 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
9224 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9225 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9226 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
9227 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
9228 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9229 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
9230 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9231 fcnZMax, QStringLiteral( "GeometryGroup" ) )
9232 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9233 fcnZMin, QStringLiteral( "GeometryGroup" ) )
9234 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9235 fcnMMax, QStringLiteral( "GeometryGroup" ) )
9236 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9237 fcnMMin, QStringLiteral( "GeometryGroup" ) )
9238 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9239 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
9240 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9241 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
9242
9243
9244 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9245 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
9246 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
9247 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
9248
9249 orderPartsFunc->setIsStaticFunction(
9250 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9251 {
9252 const QList< QgsExpressionNode *> argList = node->args()->list();
9253 for ( QgsExpressionNode *argNode : argList )
9254 {
9255 if ( !argNode->isStatic( parent, context ) )
9256 return false;
9257 }
9258
9259 if ( node->args()->count() > 1 )
9260 {
9261 QgsExpressionNode *argNode = node->args()->at( 1 );
9262
9263 QString expString = argNode->eval( parent, context ).toString();
9264
9265 QgsExpression e( expString );
9266
9267 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9268 return true;
9269 }
9270
9271 return true;
9272 } );
9273
9274 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9275 {
9276 if ( node->args()->count() > 1 )
9277 {
9278 QgsExpressionNode *argNode = node->args()->at( 1 );
9279 QString expression = argNode->eval( parent, context ).toString();
9281 e.prepare( context );
9282 context->setCachedValue( expression, QVariant::fromValue( e ) );
9283 }
9284 return true;
9285 }
9286 );
9287 functions << orderPartsFunc;
9288
9289 functions
9290 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9291 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9292 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
9293 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9294 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9295 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
9296 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9297 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
9298 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9299 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9300 fcnLineInterpolatePointByM, QStringLiteral( "GeometryGroup" ) )
9301 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9302 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
9303 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9304 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
9305 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9306 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9307 fcnLineLocateM, QStringLiteral( "GeometryGroup" ) )
9308 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9309 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
9310 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9311 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
9312 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9313 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
9314
9315
9316 // **Record** functions
9317
9318 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
9319 idFunc->setIsStatic( false );
9320 functions << idFunc;
9321
9322 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
9323 currentFeatureFunc->setIsStatic( false );
9324 functions << currentFeatureFunc;
9325
9326 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QStringLiteral( "WithBraces" ) ), fcnUuid, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$uuid" ) );
9327 uuidFunc->setIsStatic( false );
9328 functions << uuidFunc;
9329
9330 functions
9331 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
9332 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9333 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
9334 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
9335 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
9336 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9337 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
9338 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
9339
9340 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
9341 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9342 attributesFunc->setIsStatic( false );
9343 functions << attributesFunc;
9344 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
9345 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9346 representAttributesFunc->setIsStatic( false );
9347 functions << representAttributesFunc;
9348
9349 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
9350 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9351 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9352 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9353 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9354 validateFeature->setIsStatic( false );
9355 functions << validateFeature;
9356
9357 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
9358 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
9359 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9360 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9361 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9362 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9363 validateAttribute->setIsStatic( false );
9364 functions << validateAttribute;
9365
9367 QStringLiteral( "maptip" ),
9368 -1,
9369 fcnFeatureMaptip,
9370 QStringLiteral( "Record and Attributes" ),
9371 QString(),
9372 false,
9373 QSet<QString>()
9374 );
9375 maptipFunc->setIsStatic( false );
9376 functions << maptipFunc;
9377
9379 QStringLiteral( "display_expression" ),
9380 -1,
9381 fcnFeatureDisplayExpression,
9382 QStringLiteral( "Record and Attributes" ),
9383 QString(),
9384 false,
9385 QSet<QString>()
9386 );
9387 displayFunc->setIsStatic( false );
9388 functions << displayFunc;
9389
9391 QStringLiteral( "is_selected" ),
9392 -1,
9393 fcnIsSelected,
9394 QStringLiteral( "Record and Attributes" ),
9395 QString(),
9396 false,
9397 QSet<QString>()
9398 );
9399 isSelectedFunc->setIsStatic( false );
9400 functions << isSelectedFunc;
9401
9402 functions
9404 QStringLiteral( "num_selected" ),
9405 -1,
9406 fcnNumSelected,
9407 QStringLiteral( "Record and Attributes" ),
9408 QString(),
9409 false,
9410 QSet<QString>()
9411 );
9412
9413 functions
9415 QStringLiteral( "sqlite_fetch_and_increment" ),
9417 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
9418 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
9419 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
9420 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
9421 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
9422 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
9423 fcnSqliteFetchAndIncrement,
9424 QStringLiteral( "Record and Attributes" )
9425 );
9426
9427 // **CRS** functions
9428 functions
9429 << new QgsStaticExpressionFunction( QStringLiteral( "crs_to_authid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "crs" ) ), fcnCrsToAuthid, QStringLiteral( "CRS" ), QString(), true )
9430 << new QgsStaticExpressionFunction( QStringLiteral( "crs_from_text" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "definition" ) ), fcnCrsFromText, QStringLiteral( "CRS" ) );
9431
9432
9433 // **Fields and Values** functions
9434 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
9435
9436 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9437 {
9438 Q_UNUSED( context )
9439 if ( node->args()->count() == 1 )
9440 {
9441 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9442 if ( colRef )
9443 {
9444 return true;
9445 }
9446 else
9447 {
9448 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9449 return false;
9450 }
9451 }
9452 else if ( node->args()->count() == 2 )
9453 {
9454 return true;
9455 }
9456 else
9457 {
9458 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9459 return false;
9460 }
9461 }
9462 );
9463
9464 functions << representValueFunc;
9465
9466 // **General** functions
9467 functions
9468 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9469 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
9470 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
9471 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
9473 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9474 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
9475 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
9476 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
9478 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
9479 fcnMimeType, QStringLiteral( "General" ) )
9480 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9481 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
9482 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
9483
9484 // **var** function
9485 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
9486 varFunction->setIsStaticFunction(
9487 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9488 {
9489 /* A variable node is static if it has a static name and the name can be found at prepare
9490 * time and is tagged with isStatic.
9491 * It is not static if a variable is set during iteration or not tagged isStatic.
9492 * (e.g. geom_part variable)
9493 */
9494 if ( node->args()->count() > 0 )
9495 {
9496 QgsExpressionNode *argNode = node->args()->at( 0 );
9497
9498 if ( !argNode->isStatic( parent, context ) )
9499 return false;
9500
9501 const QString varName = argNode->eval( parent, context ).toString();
9502 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
9503 return false;
9504
9505 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9506 return scope ? scope->isStatic( varName ) : false;
9507 }
9508 return false;
9509 }
9510 );
9511 varFunction->setUsesGeometryFunction(
9512 []( const QgsExpressionNodeFunction * node ) -> bool
9513 {
9514 if ( node && node->args()->count() > 0 )
9515 {
9516 QgsExpressionNode *argNode = node->args()->at( 0 );
9517 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9518 {
9519 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
9520 return true;
9521 }
9522 }
9523 return false;
9524 }
9525 );
9526
9527 functions
9528 << varFunction;
9529
9530 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9531 evalTemplateFunction->setIsStaticFunction(
9532 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9533 {
9534 if ( node->args()->count() > 0 )
9535 {
9536 QgsExpressionNode *argNode = node->args()->at( 0 );
9537
9538 if ( argNode->isStatic( parent, context ) )
9539 {
9540 QString expString = argNode->eval( parent, context ).toString();
9541
9542 QgsExpression e( expString );
9543
9544 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9545 return true;
9546 }
9547 }
9548
9549 return false;
9550 } );
9551 functions << evalTemplateFunction;
9552
9553 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9554 evalFunc->setIsStaticFunction(
9555 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9556 {
9557 if ( node->args()->count() > 0 )
9558 {
9559 QgsExpressionNode *argNode = node->args()->at( 0 );
9560
9561 if ( argNode->isStatic( parent, context ) )
9562 {
9563 QString expString = argNode->eval( parent, context ).toString();
9564
9565 QgsExpression e( expString );
9566
9567 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9568 return true;
9569 }
9570 }
9571
9572 return false;
9573 } );
9574
9575 functions << evalFunc;
9576
9577 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9578 attributeFunc->setIsStaticFunction(
9579 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9580 {
9581 const QList< QgsExpressionNode *> argList = node->args()->list();
9582 for ( QgsExpressionNode *argNode : argList )
9583 {
9584 if ( !argNode->isStatic( parent, context ) )
9585 return false;
9586 }
9587
9588 if ( node->args()->count() == 1 )
9589 {
9590 // not static -- this is the variant which uses the current feature taken direct from the expression context
9591 return false;
9592 }
9593
9594 return true;
9595 } );
9596 functions << attributeFunc;
9597
9598 functions
9599 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9601 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9602 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9603
9604 // functions for arrays
9607 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9608 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9609 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9610 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9611 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9612 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9613 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9614 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9615 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9616 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9617 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9618 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9619 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9620 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9621 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9622 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9623 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9624 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9625 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9626 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9627 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9628 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9629 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9630 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9631 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9632 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9633 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9634 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9635 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9636 << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) )
9637 << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) )
9638 << new QgsStaticExpressionFunction( QStringLiteral( "generate_series" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "stop" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "step" ), true, 1.0 ), fcnGenerateSeries, QStringLiteral( "Arrays" ) )
9639 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9640
9641 //functions for maps
9642 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9643 << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "map_to_json" ) )
9644 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9645 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9646 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9647 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9648 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9649 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9650 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9651 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9652 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9653 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9654 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9655 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9656 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9657 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9658 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9659 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9660 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9661 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9662 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9663
9664 ;
9665
9667
9668 //QgsExpression has ownership of all built-in functions
9669 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9670 {
9671 *sOwnedFunctions() << func;
9672 *sBuiltinFunctions() << func->name();
9673 sBuiltinFunctions()->append( func->aliases() );
9674 }
9675 }
9676 return functions;
9677}
9678
9679bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9680{
9681 int fnIdx = functionIndex( function->name() );
9682 if ( fnIdx != -1 )
9683 {
9684 return false;
9685 }
9686
9687 QMutexLocker locker( &sFunctionsMutex );
9688 sFunctions()->append( function );
9689 if ( transferOwnership )
9690 sOwnedFunctions()->append( function );
9691
9692 return true;
9693}
9694
9695bool QgsExpression::unregisterFunction( const QString &name )
9696{
9697 // You can never override the built in functions.
9698 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9699 {
9700 return false;
9701 }
9702 int fnIdx = functionIndex( name );
9703 if ( fnIdx != -1 )
9704 {
9705 QMutexLocker locker( &sFunctionsMutex );
9706 sFunctions()->removeAt( fnIdx );
9707 sFunctionIndexMap.clear();
9708 return true;
9709 }
9710 return false;
9711}
9712
9714{
9715 qDeleteAll( *sOwnedFunctions() );
9716 sOwnedFunctions()->clear();
9718
9719const QStringList &QgsExpression::BuiltinFunctions()
9720{
9721 if ( sBuiltinFunctions()->isEmpty() )
9722 {
9723 Functions(); // this method builds the gmBuiltinFunctions as well
9724 }
9725 return *sBuiltinFunctions();
9727
9729 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9730 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9731 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9732 QStringLiteral( "Arrays" ) )
9733{
9734
9735}
9736
9738{
9739 bool isStatic = false;
9740
9741 QgsExpressionNode::NodeList *args = node->args();
9743 if ( args->count() < 2 )
9744 return false;
9745
9746 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9747 {
9748 isStatic = true;
9749 }
9750 return isStatic;
9751}
9752
9754{
9755 Q_UNUSED( node )
9756 QVariantList result;
9757
9758 if ( args->count() < 2 )
9759 // error
9760 return result;
9761
9762 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9763
9764 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9765 std::unique_ptr< QgsExpressionContext > tempContext;
9766 if ( !subContext )
9767 {
9768 tempContext = std::make_unique< QgsExpressionContext >();
9769 subContext = tempContext.get();
9770 }
9771
9773 subContext->appendScope( subScope );
9774
9775 int i = 0;
9776 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
9777 {
9778 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9779 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), i, true ) );
9780 result << args->at( 1 )->eval( parent, subContext );
9781 }
9782
9783 if ( context )
9784 delete subContext->popScope();
9785
9786 return result;
9787}
9788
9789QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9791 // This is a dummy function, all the real handling is in run
9792 Q_UNUSED( values )
9793 Q_UNUSED( context )
9794 Q_UNUSED( parent )
9795 Q_UNUSED( node )
9796
9797 Q_ASSERT( false );
9798 return QVariant();
9799}
9800
9802{
9803 QgsExpressionNode::NodeList *args = node->args();
9804
9805 if ( args->count() < 2 )
9806 // error
9807 return false;
9808
9809 args->at( 0 )->prepare( parent, context );
9810
9811 QgsExpressionContext subContext;
9812 if ( context )
9813 subContext = *context;
9816 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9817 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), QVariant(), true ) );
9818 subContext.appendScope( subScope );
9819
9820 args->at( 1 )->prepare( parent, &subContext );
9821
9822 return true;
9823}
9826 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9827 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9828 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9829 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9830 QStringLiteral( "Arrays" ) )
9831{
9832
9833}
9834
9836{
9837 bool isStatic = false;
9838
9839 QgsExpressionNode::NodeList *args = node->args();
9841 if ( args->count() < 2 )
9842 return false;
9843
9844 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9845 {
9846 isStatic = true;
9847 }
9848 return isStatic;
9849}
9850
9852{
9853 Q_UNUSED( node )
9854 QVariantList result;
9855
9856 if ( args->count() < 2 )
9857 // error
9858 return result;
9859
9860 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9861
9862 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9863 std::unique_ptr< QgsExpressionContext > tempContext;
9864 if ( !subContext )
9865 {
9866 tempContext = std::make_unique< QgsExpressionContext >();
9867 subContext = tempContext.get();
9868 }
9869
9871 subContext->appendScope( subScope );
9872
9873 int limit = 0;
9874 if ( args->count() >= 3 )
9875 {
9876 const QVariant limitVar = args->at( 2 )->eval( parent, context );
9877
9878 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
9879 {
9880 limit = limitVar.toInt();
9881 }
9882 else
9883 {
9884 return result;
9885 }
9886 }
9887
9888 for ( const QVariant &value : array )
9889 {
9890 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
9891 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
9892 {
9893 result << value;
9894
9895 if ( limit > 0 && limit == result.size() )
9896 break;
9897 }
9898 }
9899
9900 if ( context )
9901 delete subContext->popScope();
9902
9903 return result;
9904}
9905
9906QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9908 // This is a dummy function, all the real handling is in run
9909 Q_UNUSED( values )
9910 Q_UNUSED( context )
9911 Q_UNUSED( parent )
9912 Q_UNUSED( node )
9913
9914 Q_ASSERT( false );
9915 return QVariant();
9916}
9917
9919{
9920 QgsExpressionNode::NodeList *args = node->args();
9921
9922 if ( args->count() < 2 )
9923 // error
9924 return false;
9925
9926 args->at( 0 )->prepare( parent, context );
9927
9928 QgsExpressionContext subContext;
9929 if ( context )
9930 subContext = *context;
9931
9933 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9934 subContext.appendScope( subScope );
9935
9936 args->at( 1 )->prepare( parent, &subContext );
9937
9938 return true;
9941 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
9942 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
9943 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
9944 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9945 QStringLiteral( "General" ) )
9946{
9947
9948}
9949
9951{
9952 bool isStatic = false;
9953
9954 QgsExpressionNode::NodeList *args = node->args();
9955
9956 if ( args->count() < 3 )
9957 return false;
9958
9959 // We only need to check if the node evaluation is static, if both - name and value - are static.
9960 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9961 {
9962 QVariant name = args->at( 0 )->eval( parent, context );
9963 QVariant value = args->at( 1 )->eval( parent, context );
9965 // Temporarily append a new scope to provide the variable
9966 appendTemporaryVariable( context, name.toString(), value );
9967 if ( args->at( 2 )->isStatic( parent, context ) )
9968 isStatic = true;
9969 popTemporaryVariable( context );
9970 }
9971
9972 return isStatic;
9973}
9974
9976{
9977 Q_UNUSED( node )
9978 QVariant result;
9979
9980 if ( args->count() < 3 )
9981 // error
9982 return result;
9983
9984 QVariant name = args->at( 0 )->eval( parent, context );
9985 QVariant value = args->at( 1 )->eval( parent, context );
9986
9987 const QgsExpressionContext *updatedContext = context;
9988 std::unique_ptr< QgsExpressionContext > tempContext;
9989 if ( !updatedContext )
9990 {
9991 tempContext = std::make_unique< QgsExpressionContext >();
9992 updatedContext = tempContext.get();
9994
9995 appendTemporaryVariable( updatedContext, name.toString(), value );
9996 result = args->at( 2 )->eval( parent, updatedContext );
9997
9998 if ( context )
9999 popTemporaryVariable( updatedContext );
10000
10001 return result;
10002}
10003
10004QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10006 // This is a dummy function, all the real handling is in run
10007 Q_UNUSED( values )
10008 Q_UNUSED( context )
10009 Q_UNUSED( parent )
10010 Q_UNUSED( node )
10011
10012 Q_ASSERT( false );
10013 return QVariant();
10014}
10015
10017{
10018 QgsExpressionNode::NodeList *args = node->args();
10019
10020 if ( args->count() < 3 )
10021 // error
10022 return false;
10023
10024 QVariant name = args->at( 0 )->prepare( parent, context );
10025 QVariant value = args->at( 1 )->prepare( parent, context );
10026
10027 const QgsExpressionContext *updatedContext = context;
10028 std::unique_ptr< QgsExpressionContext > tempContext;
10029 if ( !updatedContext )
10030 {
10031 tempContext = std::make_unique< QgsExpressionContext >();
10032 updatedContext = tempContext.get();
10033 }
10034
10035 appendTemporaryVariable( updatedContext, name.toString(), value );
10036 args->at( 2 )->prepare( parent, updatedContext );
10037
10038 if ( context )
10039 popTemporaryVariable( updatedContext );
10040
10041 return true;
10042}
10043
10044void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10045{
10046 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10047 delete updatedContext->popScope();
10048}
10049
10050void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10051{
10054
10055 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10056 updatedContext->appendScope( scope );
10057}
@ Left
Buffer to left of line.
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3185
@ ScaleDashOnly
Only dash lengths are adjusted.
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
@ ScaleGapOnly
Only gap lengths are adjusted.
@ Success
Operation succeeded.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
JoinStyle
Join styles for buffers.
Definition qgis.h:2084
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:5719
@ StdDev
Standard deviation.
@ NoStatistic
No statistic.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2071
@ Flat
Flat cap (in line with start/end of line)
@ Round
Round cap.
@ Square
Square cap (extends past start/end of line by buffer distance)
Aggregate
Available aggregates to calculate.
Definition qgis.h:5596
@ StringMinimumLength
Minimum length of string (string fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Mean
Mean of values (numeric fields only)
@ Median
Median of values (numeric fields only)
@ Max
Max of values.
@ Min
Min of values.
@ StringMaximumLength
Maximum length of string (string fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ Sum
Sum of values.
@ Minority
Minority of values.
@ CountMissing
Number of missing (null) values.
@ ArrayAggregate
Create an array of values.
@ Majority
Majority of values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ CountDistinct
Number of distinct values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3170
@ HalfDash
Start or finish the pattern with a half length dash.
@ HalfGap
Start or finish the pattern with a half length gap.
@ FullGap
Start or finish the pattern with a full gap.
@ FullDash
Start or finish the pattern with a full dash.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2130
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
@ PointM
PointM.
@ PointZ
PointZ.
@ GeometryCollection
GeometryCollection.
@ PointZM
PointZM.
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.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part 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.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of 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.
Handles the array_filter(array, expression) expression function.
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...
Handles the array loopingarray_Foreach(array, expression) expression function.
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:45
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:35
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
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
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.
QVariantMap config() const
Ellipse geometry type.
Definition qgsellipse.h:39
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.
Represents a single parameter passed to a function.
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
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.
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 int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
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)
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
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:58
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
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:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:746
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.
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.
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
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.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
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:2806
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:46
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.
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:77
QString name
Definition qgsmaplayer.h:81
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:84
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:80
QgsLayerMetadata metadata
Definition qgsmaplayer.h:83
Qgis::LayerType type
Definition qgsmaplayer.h:87
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:90
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())
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:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:692
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:558
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:426
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:105
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
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:704
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:117
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.
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:44
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:48
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.
Helper class for defining QgsExpression functions.
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.
Utility functions for working with strings.
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 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:146
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
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
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
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.
Handles the with_variable(name, value, node) expression 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...
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 QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
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:129
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6819
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6818
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6327
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6286
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
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.
Definition qgsgeometry.h:84
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:80
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition qgsgeometry.h:91
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
const QgsCoordinateReferenceSystem & crs
int precision
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:62
const QgsMapLayer * layer
Definition qgsogcutils.h:72
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:73
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30