QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
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
1551static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1552{
1553 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1554 {
1555 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1556 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1557 QVector< QPair< QString, QString > > mapItems;
1558
1559 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1560 {
1561 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1562 }
1563
1564 // larger keys should be replaced first since they may contain whole smaller keys
1565 std::sort( mapItems.begin(),
1566 mapItems.end(),
1567 []( const QPair< QString, QString > &pair1,
1568 const QPair< QString, QString > &pair2 )
1569 {
1570 return ( pair1.first.length() > pair2.first.length() );
1571 } );
1572
1573 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1574 {
1575 str = str.replace( it->first, it->second );
1576 }
1577
1578 return QVariant( str );
1579 }
1580 else if ( values.count() == 3 )
1581 {
1582 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1583 QVariantList before;
1584 QVariantList after;
1585 bool isSingleReplacement = false;
1586
1587 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1588 {
1589 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1590 }
1591 else
1592 {
1593 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1594 }
1595
1596 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1597 {
1598 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1599 isSingleReplacement = true;
1600 }
1601 else
1602 {
1603 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1604 }
1605
1606 if ( !isSingleReplacement && before.length() != after.length() )
1607 {
1608 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1609 return QVariant();
1610 }
1611
1612 for ( int i = 0; i < before.length(); i++ )
1613 {
1614 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1615 }
1616
1617 return QVariant( str );
1618 }
1619 else
1620 {
1621 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1622 return QVariant();
1623 }
1624}
1625
1626static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1627{
1628 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1629 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1630 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1631
1632 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1633 if ( !re.isValid() )
1634 {
1635 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1636 return QVariant();
1637 }
1638 return QVariant( str.replace( re, after ) );
1639}
1640
1641static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1642{
1643 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1644 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1645
1646 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1647 if ( !re.isValid() )
1648 {
1649 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1650 return QVariant();
1651 }
1652 return QVariant( ( str.indexOf( re ) + 1 ) );
1653}
1654
1655static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1656{
1657 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1658 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1659 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1660
1661 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1662 if ( !re.isValid() )
1663 {
1664 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1665 return QVariant();
1666 }
1667
1668 QRegularExpressionMatch matches = re.match( str );
1669 if ( matches.hasMatch() )
1670 {
1671 QVariantList array;
1672 QStringList list = matches.capturedTexts();
1673
1674 // Skip the first string to only return captured groups
1675 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1676 {
1677 array += ( !( *it ).isEmpty() ) ? *it : empty;
1678 }
1679
1680 return QVariant( array );
1681 }
1682 else
1683 {
1684 return QVariant();
1685 }
1686}
1687
1688static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689{
1690 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1691 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1692
1693 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1694 if ( !re.isValid() )
1695 {
1696 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1697 return QVariant();
1698 }
1699
1700 // extract substring
1701 QRegularExpressionMatch match = re.match( str );
1702 if ( match.hasMatch() )
1703 {
1704 // return first capture
1705 if ( match.lastCapturedIndex() > 0 )
1706 {
1707 // a capture group was present, so use that
1708 return QVariant( match.captured( 1 ) );
1709 }
1710 else
1711 {
1712 // no capture group, so using all match
1713 return QVariant( match.captured( 0 ) );
1714 }
1715 }
1716 else
1717 {
1718 return QVariant( "" );
1719 }
1720}
1721
1722static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1723{
1724 QString uuid = QUuid::createUuid().toString();
1725 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1726 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1727 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1728 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1729 return uuid;
1730}
1731
1732static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1733{
1734 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1735 return QVariant();
1736
1737 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1738 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1739
1740 int len = 0;
1741 if ( values.at( 2 ).isValid() )
1742 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1743 else
1744 len = str.size();
1745
1746 if ( from < 0 )
1747 {
1748 from = str.size() + from;
1749 if ( from < 0 )
1750 {
1751 from = 0;
1752 }
1753 }
1754 else if ( from > 0 )
1755 {
1756 //account for the fact that substr() starts at 1
1757 from -= 1;
1758 }
1759
1760 if ( len < 0 )
1761 {
1762 len = str.size() + len - from;
1763 if ( len < 0 )
1764 {
1765 len = 0;
1766 }
1767 }
1768
1769 return QVariant( str.mid( from, len ) );
1770}
1771static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1772{
1773 FEAT_FROM_CONTEXT( context, f )
1774 // TODO: handling of 64-bit feature ids?
1775 return QVariant( static_cast< int >( f.id() ) );
1776}
1777
1778static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1779{
1780 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1781 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1782 bool foundLayer = false;
1783 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1784 {
1785 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1786 if ( !layer || !layer->dataProvider() )
1787 {
1788 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1789 return QVariant();
1790 }
1791
1792 if ( bandNb < 1 || bandNb > layer->bandCount() )
1793 {
1794 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1795 return QVariant();
1796 }
1797
1798 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1799 {
1800 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1801 return QVariant();
1802 }
1803
1804 QgsPointXY point = geom.asPoint();
1805 if ( geom.isMultipart() )
1806 {
1807 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1808 if ( multiPoint.count() == 1 )
1809 {
1810 point = multiPoint[0];
1811 }
1812 else
1813 {
1814 // if the geometry contains more than one part, return an undefined value
1815 return QVariant();
1816 }
1817 }
1818
1819 double value = layer->dataProvider()->sample( point, bandNb );
1820 return std::isnan( value ) ? QVariant() : value;
1821 },
1822 foundLayer );
1823
1824 if ( !foundLayer )
1825 {
1826 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1827 return QVariant();
1828 }
1829 else
1830 {
1831 return res;
1832 }
1833}
1834
1835static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1836{
1837 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1838 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1839
1840 bool foundLayer = false;
1841 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1842 {
1843 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1844 if ( !layer || !layer->dataProvider() )
1845 {
1846 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1847 return QVariant();
1848 }
1849
1850 if ( bandNb < 1 || bandNb > layer->bandCount() )
1851 {
1852 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1853 return QVariant();
1854 }
1855
1856 if ( std::isnan( value ) )
1857 {
1858 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1859 return QVariant();
1860 }
1861
1862 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1863 {
1864 return QVariant();
1865 }
1866
1867 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1868 if ( data.isEmpty() )
1869 {
1870 return QVariant();
1871 }
1872
1873 QVariantMap result;
1874 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1875 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1876 {
1877 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1878 if ( field.isColor() || field.isRamp() )
1879 {
1880 continue;
1881 }
1882 result.insert( fields.at( idx ).name, data.at( idx ) );
1883 }
1884
1885 return result;
1886 }, foundLayer );
1887
1888 if ( !foundLayer )
1889 {
1890 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1891 return QVariant();
1892 }
1893 else
1894 {
1895 return res;
1896 }
1897}
1898
1899static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1900{
1901 if ( !context )
1902 return QVariant();
1903
1904 return context->feature();
1905}
1906
1907static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1908{
1909 QgsFeature feature;
1910 QString attr;
1911 if ( values.size() == 1 )
1912 {
1913 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1914 feature = context->feature();
1915 }
1916 else if ( values.size() == 2 )
1917 {
1918 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1919 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1920 }
1921 else
1922 {
1923 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1924 return QVariant();
1925 }
1926
1927 return feature.attribute( attr );
1928}
1929
1930static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1931{
1932 QString table { R"html(
1933 <table>
1934 <thead>
1935 <tr><th>%1</th></tr>
1936 </thead>
1937 <tbody>
1938 <tr><td>%2</td></tr>
1939 </tbody>
1940 </table>)html" };
1941 QVariantMap dict;
1942 if ( values.size() == 1 )
1943 {
1944 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1945 }
1946 else
1947 {
1948 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1949 return QVariant();
1950 }
1951
1952 if ( dict.isEmpty() )
1953 {
1954 return QVariant();
1955 }
1956
1957 QStringList headers;
1958 QStringList cells;
1959
1960 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1961 {
1962 headers.push_back( it.key().toHtmlEscaped() );
1963 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1964 }
1965
1966 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1967}
1968
1969static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1970{
1971 QString table { R"html(
1972 <dl>
1973 %1
1974 </dl>)html" };
1975 QVariantMap dict;
1976 if ( values.size() == 1 )
1977 {
1978 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1979 }
1980 else
1981 {
1982 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1983 return QVariant();
1984 }
1985
1986 if ( dict.isEmpty() )
1987 {
1988 return QVariant();
1989 }
1990
1991 QString rows;
1992
1993 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1994 {
1995 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
1996 }
1997
1998 return table.arg( rows );
1999}
2000
2001static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2002{
2003 QVariant layer;
2004 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2005 {
2006 layer = context->variable( QStringLiteral( "layer" ) );
2007 }
2008 else
2009 {
2010 //first node is layer id or name
2011 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2013 layer = node->eval( parent, context );
2015 }
2016
2017 QgsFeature feature;
2018 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2019 {
2020 feature = context->feature();
2021 }
2022 else
2023 {
2024 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2025 }
2026
2028 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2029 if ( strength == QLatin1String( "hard" ) )
2030 {
2032 }
2033 else if ( strength == QLatin1String( "soft" ) )
2034 {
2036 }
2037
2038 bool foundLayer = false;
2039 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2040 {
2041 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2042 if ( !layer )
2043 {
2044 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2045 return QVariant();
2046 }
2047
2048 const QgsFields fields = layer->fields();
2049 bool valid = true;
2050 for ( int i = 0; i < fields.size(); i++ )
2051 {
2052 QStringList errors;
2053 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2054 if ( !valid )
2055 {
2056 break;
2057 }
2058 }
2059
2060 return valid;
2061 }, foundLayer );
2062
2063 if ( !foundLayer )
2064 {
2065 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2066 return QVariant();
2067 }
2068
2069 return res;
2070}
2071
2072static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2073{
2074 QVariant layer;
2075 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2076 {
2077 layer = context->variable( QStringLiteral( "layer" ) );
2078 }
2079 else
2080 {
2081 //first node is layer id or name
2082 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2084 layer = node->eval( parent, context );
2086 }
2087
2088 QgsFeature feature;
2089 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2090 {
2091 feature = context->feature();
2092 }
2093 else
2094 {
2095 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2096 }
2097
2099 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2100 if ( strength == QLatin1String( "hard" ) )
2101 {
2103 }
2104 else if ( strength == QLatin1String( "soft" ) )
2105 {
2107 }
2108
2109 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2110
2111 bool foundLayer = false;
2112 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2113 {
2114 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2115 if ( !layer )
2116 {
2117 return QVariant();
2118 }
2119
2120 const int fieldIndex = layer->fields().indexFromName( attributeName );
2121 if ( fieldIndex == -1 )
2122 {
2123 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2124 return QVariant();
2125 }
2126
2127 QStringList errors;
2128 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2129 return valid;
2130 }, foundLayer );
2131
2132 if ( !foundLayer )
2133 {
2134 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2135 return QVariant();
2136 }
2137
2138 return res;
2139}
2140
2141static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2142{
2143 QgsFeature feature;
2144 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2145 {
2146 feature = context->feature();
2147 }
2148 else
2149 {
2150 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2151 }
2152
2153 const QgsFields fields = feature.fields();
2154 QVariantMap result;
2155 for ( int i = 0; i < fields.count(); ++i )
2156 {
2157 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2158 }
2159 return result;
2160}
2161
2162static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2163{
2164 QgsVectorLayer *layer = nullptr;
2165 QgsFeature feature;
2166
2167 // TODO this expression function is NOT thread safe
2169 if ( values.isEmpty() )
2170 {
2171 feature = context->feature();
2172 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2173 }
2174 else if ( values.size() == 1 )
2175 {
2176 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2177 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2178 }
2179 else if ( values.size() == 2 )
2180 {
2181 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2182 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2183 }
2184 else
2185 {
2186 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2187 return QVariant();
2188 }
2190
2191 if ( !layer )
2192 {
2193 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2194 return QVariant();
2195 }
2196
2197 if ( !feature.isValid() )
2198 {
2199 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2200 return QVariant();
2201 }
2202
2203 const QgsFields fields = feature.fields();
2204 QVariantMap result;
2205 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2206 {
2207 const QString fieldName { fields.at( fieldIndex ).name() };
2208 const QVariant attributeVal = feature.attribute( fieldIndex );
2209 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2210 if ( context && context->hasCachedValue( cacheValueKey ) )
2211 {
2212 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2213 }
2214 else
2215 {
2216 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2218 QVariant cache;
2219 if ( context )
2220 {
2221 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2222
2223 if ( !context->hasCachedValue( cacheKey ) )
2224 {
2225 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2226 context->setCachedValue( cacheKey, cache );
2227 }
2228 else
2229 {
2230 cache = context->cachedValue( cacheKey );
2231 }
2232 }
2233 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2234
2235 result.insert( fields.at( fieldIndex ).name(), value );
2236
2237 if ( context )
2238 {
2239 context->setCachedValue( cacheValueKey, value );
2240 }
2241
2242 }
2243 }
2244 return result;
2245}
2246
2247static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2248{
2249 QgsVectorLayer *layer = nullptr;
2250 QgsFeature feature;
2251 bool evaluate = true;
2252
2253 // TODO this expression function is NOT thread safe
2255 if ( values.isEmpty() )
2256 {
2257 feature = context->feature();
2258 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2259 }
2260 else if ( values.size() == 1 )
2261 {
2262 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2263 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2264 }
2265 else if ( values.size() == 2 )
2266 {
2267 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2268 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2269 }
2270 else if ( values.size() == 3 )
2271 {
2272 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2273 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2274 evaluate = values.value( 2 ).toBool();
2275 }
2276 else
2277 {
2278 if ( isMaptip )
2279 {
2280 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2281 }
2282 else
2283 {
2284 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2285 }
2286 return QVariant();
2287 }
2288
2289 if ( !layer )
2290 {
2291 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2292 return QVariant( );
2293 }
2295
2296 if ( !feature.isValid() )
2297 {
2298 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2299 return QVariant( );
2300 }
2301
2302 if ( ! evaluate )
2303 {
2304 if ( isMaptip )
2305 {
2306 return layer->mapTipTemplate();
2307 }
2308 else
2309 {
2310 return layer->displayExpression();
2311 }
2312 }
2313
2314 QgsExpressionContext subContext( *context );
2315 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2316 subContext.setFeature( feature );
2317
2318 if ( isMaptip )
2319 {
2320 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2321 }
2322 else
2323 {
2324 QgsExpression exp( layer->displayExpression() );
2325 exp.prepare( &subContext );
2326 return exp.evaluate( &subContext ).toString();
2327 }
2328}
2329
2330static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2331{
2332 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2333}
2334
2335static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2336{
2337 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2338}
2339
2340static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2341{
2342 QgsFeature feature;
2343 QVariant layer;
2344 if ( values.isEmpty() )
2345 {
2346 feature = context->feature();
2347 layer = context->variable( QStringLiteral( "layer" ) );
2348 }
2349 else if ( values.size() == 1 )
2350 {
2351 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2352 layer = context->variable( QStringLiteral( "layer" ) );
2353 }
2354 else if ( values.size() == 2 )
2355 {
2356 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2357 layer = values.at( 0 );
2358 }
2359 else
2360 {
2361 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2362 return QVariant();
2363 }
2364
2365 bool foundLayer = false;
2366 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2367 {
2368 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2369 if ( !layer || !feature.isValid() )
2370 {
2371 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2372 }
2373
2374 return layer->selectedFeatureIds().contains( feature.id() );
2375 }, foundLayer );
2376 if ( !foundLayer )
2377 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2378 else
2379 return res;
2380}
2381
2382static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2383{
2384 QVariant layer;
2385
2386 if ( values.isEmpty() )
2387 layer = context->variable( QStringLiteral( "layer" ) );
2388 else if ( values.count() == 1 )
2389 layer = values.at( 0 );
2390 else
2391 {
2392 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2393 return QVariant();
2394 }
2395
2396 bool foundLayer = false;
2397 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2398 {
2399 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2400 if ( !layer )
2401 {
2402 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2403 }
2404
2405 return layer->selectedFeatureCount();
2406 }, foundLayer );
2407 if ( !foundLayer )
2408 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2409 else
2410 return res;
2411}
2412
2413static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2414{
2415 static QMap<QString, qlonglong> counterCache;
2416 QVariant functionResult;
2417
2418 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2419 {
2420 QString database;
2421
2422 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2423
2424 if ( layer )
2425 {
2426 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2427 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2428 if ( database.isEmpty() )
2429 {
2430 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2431 }
2432 }
2433 else
2434 {
2435 database = databaseArgument;
2436 }
2437
2438 const QString table = values.at( 1 ).toString();
2439 const QString idColumn = values.at( 2 ).toString();
2440 const QString filterAttribute = values.at( 3 ).toString();
2441 const QVariant filterValue = values.at( 4 ).toString();
2442 const QVariantMap defaultValues = values.at( 5 ).toMap();
2443
2444 // read from database
2446 sqlite3_statement_unique_ptr sqliteStatement;
2447
2448 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2449 {
2450 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2451 functionResult = QVariant();
2452 return;
2453 }
2454
2455 QString errorMessage;
2456 QString currentValSql;
2457
2458 qlonglong nextId = 0;
2459 bool cachedMode = false;
2460 bool valueRetrieved = false;
2461
2462 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2463
2464 // Running in transaction mode, check for cached value first
2465 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2466 {
2467 cachedMode = true;
2468
2469 auto cachedCounter = counterCache.find( cacheString );
2470
2471 if ( cachedCounter != counterCache.end() )
2472 {
2473 qlonglong &cachedValue = cachedCounter.value();
2474 nextId = cachedValue;
2475 nextId += 1;
2476 cachedValue = nextId;
2477 valueRetrieved = true;
2478 }
2479 }
2480
2481 // Either not in cached mode or no cached value found, obtain from DB
2482 if ( !cachedMode || !valueRetrieved )
2483 {
2484 int result = SQLITE_ERROR;
2485
2486 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2487 if ( !filterAttribute.isNull() )
2488 {
2489 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2490 }
2491
2492 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2493
2494 if ( result == SQLITE_OK )
2495 {
2496 nextId = 0;
2497 if ( sqliteStatement.step() == SQLITE_ROW )
2498 {
2499 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2500 }
2501
2502 // If in cached mode: add value to cache and connect to transaction
2503 if ( cachedMode && result == SQLITE_OK )
2504 {
2505 counterCache.insert( cacheString, nextId );
2506
2507 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2508 {
2509 counterCache.remove( cacheString );
2510 } );
2511 }
2512 valueRetrieved = true;
2513 }
2514 }
2515
2516 if ( valueRetrieved )
2517 {
2518 QString upsertSql;
2519 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2520 QStringList cols;
2521 QStringList vals;
2522 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2523 vals << QgsSqliteUtils::quotedValue( nextId );
2524
2525 if ( !filterAttribute.isNull() )
2526 {
2527 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2528 vals << QgsSqliteUtils::quotedValue( filterValue );
2529 }
2530
2531 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2532 {
2533 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2534 vals << iter.value().toString();
2535 }
2536
2537 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2538 upsertSql += QLatin1String( " VALUES " );
2539 upsertSql += '(' + vals.join( ',' ) + ')';
2540
2541 int result = SQLITE_ERROR;
2542 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2543 {
2544 QgsTransaction *transaction = layer->dataProvider()->transaction();
2545 if ( transaction->executeSql( upsertSql, errorMessage ) )
2546 {
2547 result = SQLITE_OK;
2548 }
2549 }
2550 else
2551 {
2552 result = sqliteDb.exec( upsertSql, errorMessage );
2553 }
2554 if ( result == SQLITE_OK )
2555 {
2556 functionResult = QVariant( nextId );
2557 return;
2558 }
2559 else
2560 {
2561 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2562 functionResult = QVariant();
2563 return;
2564 }
2565 }
2566
2567 functionResult = QVariant();
2568 };
2569
2570 bool foundLayer = false;
2571 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2572 {
2573 fetchAndIncrementFunc( layer, QString() );
2574 }, foundLayer );
2575 if ( !foundLayer )
2576 {
2577 const QString databasePath = values.at( 0 ).toString();
2578 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2579 {
2580 fetchAndIncrementFunc( nullptr, databasePath );
2581 } );
2582 }
2583
2584 return functionResult;
2585}
2586
2587static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2588{
2589 QString concat;
2590 for ( const QVariant &value : values )
2591 {
2592 if ( !QgsVariantUtils::isNull( value ) )
2593 concat += QgsExpressionUtils::getStringValue( value, parent );
2594 }
2595 return concat;
2596}
2597
2598static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2599{
2600 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2601 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2602}
2603
2604static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2605{
2606 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2607 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2608 return string.right( pos );
2609}
2610
2611static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2612{
2613 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2614 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2615 return string.left( pos );
2616}
2617
2618static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2619{
2620 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2621 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2622 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2623 return string.leftJustified( length, fill.at( 0 ), true );
2624}
2625
2626static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2627{
2628 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2629 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2630 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2631 return string.rightJustified( length, fill.at( 0 ), true );
2632}
2633
2634static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2635{
2636 if ( values.size() < 1 )
2637 {
2638 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2639 return QVariant();
2640 }
2641
2642 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2643 for ( int n = 1; n < values.length(); n++ )
2644 {
2645 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2646 }
2647 return string;
2648}
2649
2650
2651static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2652{
2653 return QVariant( QDateTime::currentDateTime() );
2654}
2655
2656static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2657{
2658 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2659 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2660 if ( format.isEmpty() && !language.isEmpty() )
2661 {
2662 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2663 return QVariant( QDate() );
2664 }
2665
2666 if ( format.isEmpty() && language.isEmpty() )
2667 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2668
2669 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2670 QLocale locale = QLocale();
2671 if ( !language.isEmpty() )
2672 {
2673 locale = QLocale( language );
2674 }
2675
2676 QDate date = locale.toDate( datestring, format );
2677 if ( !date.isValid() )
2678 {
2679 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2680 date = QDate();
2681 }
2682 return QVariant( date );
2683}
2684
2685static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2686{
2687 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2688 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2689 if ( format.isEmpty() && !language.isEmpty() )
2690 {
2691 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2692 return QVariant( QTime() );
2693 }
2694
2695 if ( format.isEmpty() && language.isEmpty() )
2696 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2697
2698 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2699 QLocale locale = QLocale();
2700 if ( !language.isEmpty() )
2701 {
2702 locale = QLocale( language );
2703 }
2704
2705 QTime time = locale.toTime( timestring, format );
2706 if ( !time.isValid() )
2707 {
2708 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2709 time = QTime();
2710 }
2711 return QVariant( time );
2712}
2713
2714static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2715{
2716 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2717}
2718
2719/*
2720 * DMS functions
2721 */
2722
2723static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2724{
2725 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2726 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2727 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2728
2729 QString formatString;
2730 if ( values.count() > 3 )
2731 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2732
2734 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2735 {
2737 }
2738 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2739 {
2741 }
2742 else if ( ! formatString.isEmpty() )
2743 {
2744 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2745 return QVariant();
2746 }
2747
2748 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2749 {
2750 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2751 }
2752 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2753 {
2754 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2755 }
2756 else
2757 {
2758 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2759 return QVariant();
2760 }
2761}
2762
2763static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2764{
2766 return floatToDegreeFormat( format, values, context, parent, node );
2767}
2768
2769static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2770{
2771 double value = 0.0;
2772 bool ok = false;
2773 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2774
2775 return ok ? QVariant( value ) : QVariant();
2776}
2777
2778static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2779{
2781 return floatToDegreeFormat( format, values, context, parent, node );
2782}
2783
2784static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2785{
2786 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2787 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2788 qint64 seconds = d2.secsTo( d1 );
2789 return QVariant::fromValue( QgsInterval( seconds ) );
2790}
2791
2792static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2793{
2794 if ( !values.at( 0 ).canConvert<QDate>() )
2795 return QVariant();
2796
2797 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2798 if ( !date.isValid() )
2799 return QVariant();
2800
2801 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2802 // (to match PostgreSQL behavior)
2803 return date.dayOfWeek() % 7;
2804}
2805
2806static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2807{
2808 QVariant value = values.at( 0 );
2809 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2810 if ( inter.isValid() )
2811 {
2812 return QVariant( inter.days() );
2813 }
2814 else
2815 {
2816 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2817 return QVariant( d1.date().day() );
2818 }
2819}
2820
2821static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2822{
2823 QVariant value = values.at( 0 );
2824 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2825 if ( inter.isValid() )
2826 {
2827 return QVariant( inter.years() );
2828 }
2829 else
2830 {
2831 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2832 return QVariant( d1.date().year() );
2833 }
2834}
2835
2836static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2837{
2838 QVariant value = values.at( 0 );
2839 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2840 if ( inter.isValid() )
2841 {
2842 return QVariant( inter.months() );
2843 }
2844 else
2845 {
2846 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2847 return QVariant( d1.date().month() );
2848 }
2849}
2850
2851static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2852{
2853 QVariant value = values.at( 0 );
2854 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2855 if ( inter.isValid() )
2856 {
2857 return QVariant( inter.weeks() );
2858 }
2859 else
2860 {
2861 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2862 return QVariant( d1.date().weekNumber() );
2863 }
2864}
2865
2866static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2867{
2868 QVariant value = values.at( 0 );
2869 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2870 if ( inter.isValid() )
2871 {
2872 return QVariant( inter.hours() );
2873 }
2874 else
2875 {
2876 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2877 return QVariant( t1.hour() );
2878 }
2879}
2880
2881static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2882{
2883 QVariant value = values.at( 0 );
2884 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2885 if ( inter.isValid() )
2886 {
2887 return QVariant( inter.minutes() );
2888 }
2889 else
2890 {
2891 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2892 return QVariant( t1.minute() );
2893 }
2894}
2895
2896static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2897{
2898 QVariant value = values.at( 0 );
2899 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2900 if ( inter.isValid() )
2901 {
2902 return QVariant( inter.seconds() );
2903 }
2904 else
2905 {
2906 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2907 return QVariant( t1.second() );
2908 }
2909}
2910
2911static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2912{
2913 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2914 if ( dt.isValid() )
2915 {
2916 return QVariant( dt.toMSecsSinceEpoch() );
2917 }
2918 else
2919 {
2920 return QVariant();
2921 }
2922}
2923
2924static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2925{
2926 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2927 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2928 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2929}
2930
2931static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2932{
2933 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2934 if ( parent->hasEvalError() )
2935 {
2936 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2937 return QVariant();
2938 }
2939 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2940 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2941}
2942
2943static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2945 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2946 if ( parent->hasEvalError() )
2947 {
2948 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2949 return QVariant();
2950 }
2951 bool ok;
2952 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2953}
2954
2955#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2956 if ( !(f).hasGeometry() ) \
2957 return QVariant(); \
2958 QgsGeometry g = (f).geometry(); \
2959 if ( (g).type() != (geomtype) ) \
2960 return QVariant();
2961
2962static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2963{
2964 FEAT_FROM_CONTEXT( context, f )
2966 if ( g.isMultipart() )
2967 {
2968 return g.asMultiPoint().at( 0 ).x();
2969 }
2970 else
2971 {
2972 return g.asPoint().x();
2973 }
2974}
2975
2976static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2977{
2978 FEAT_FROM_CONTEXT( context, f )
2980 if ( g.isMultipart() )
2981 {
2982 return g.asMultiPoint().at( 0 ).y();
2983 }
2984 else
2985 {
2986 return g.asPoint().y();
2987 }
2988}
2989
2990static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2991{
2992 FEAT_FROM_CONTEXT( context, f )
2994
2995 if ( g.isEmpty() )
2996 return QVariant();
2997
2998 const QgsAbstractGeometry *abGeom = g.constGet();
2999
3000 if ( g.isEmpty() || !abGeom->is3D() )
3001 return QVariant();
3002
3003 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3004 {
3005 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3006 if ( point )
3007 return point->z();
3008 }
3009 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3010 {
3011 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3012 {
3013 if ( collection->numGeometries() > 0 )
3014 {
3015 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3016 return point->z();
3017 }
3018 }
3019 }
3020
3021 return QVariant();
3022}
3023
3024static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3025{
3026 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3027 if ( geom.isNull() )
3028 return QVariant();
3029
3030 bool isValid = geom.isGeosValid();
3031
3032 return QVariant( isValid );
3033}
3034
3035static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3036{
3037 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3038 if ( geom.isNull() )
3039 return QVariant();
3040
3041 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3042#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3044#else
3046#endif
3047 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3049 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3051
3052 const bool keepCollapsed = values.value( 2 ).toBool();
3053
3054 QgsGeometry valid;
3055 try
3056 {
3057 valid = geom.makeValid( method, keepCollapsed );
3058 }
3059 catch ( QgsNotSupportedException & )
3060 {
3061 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3062 return QVariant();
3063 }
3064
3065 return QVariant::fromValue( valid );
3066}
3067
3068static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3069{
3070 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3071 if ( geom.isNull() )
3072 return QVariant();
3073
3074 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3075 QVariantList array;
3076 for ( int i = 0; i < multiGeom.size(); ++i )
3077 {
3078 array += QVariant::fromValue( multiGeom.at( i ) );
3079 }
3080
3081 return array;
3082}
3083
3084static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3085{
3086 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3087 if ( geom.isNull() )
3088 return QVariant();
3089
3090 //if single point, return the point's x coordinate
3091 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3092 {
3093 return geom.asPoint().x();
3094 }
3095
3096 //otherwise return centroid x
3097 QgsGeometry centroid = geom.centroid();
3098 QVariant result( centroid.asPoint().x() );
3099 return result;
3100}
3101
3102static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3103{
3104 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3105 if ( geom.isNull() )
3106 return QVariant();
3107
3108 //if single point, return the point's y coordinate
3109 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3110 {
3111 return geom.asPoint().y();
3112 }
3113
3114 //otherwise return centroid y
3115 QgsGeometry centroid = geom.centroid();
3116 QVariant result( centroid.asPoint().y() );
3117 return result;
3118}
3119
3120static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3121{
3122 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3123 if ( geom.isNull() )
3124 return QVariant(); //or 0?
3125
3126 if ( !geom.constGet()->is3D() )
3127 return QVariant();
3128
3129 //if single point, return the point's z coordinate
3130 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3131 {
3132 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3133 if ( point )
3134 return point->z();
3135 }
3136 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3137 {
3138 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3139 {
3140 if ( collection->numGeometries() == 1 )
3141 {
3142 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3143 return point->z();
3144 }
3145 }
3146 }
3147
3148 return QVariant();
3149}
3150
3151static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3152{
3153 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3154 if ( geom.isNull() )
3155 return QVariant(); //or 0?
3156
3157 if ( !geom.constGet()->isMeasure() )
3158 return QVariant();
3159
3160 //if single point, return the point's m value
3161 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3162 {
3163 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3164 if ( point )
3165 return point->m();
3166 }
3167 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3168 {
3169 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3170 {
3171 if ( collection->numGeometries() == 1 )
3172 {
3173 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3174 return point->m();
3175 }
3176 }
3177 }
3178
3179 return QVariant();
3180}
3181
3182static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3183{
3184 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3185
3186 if ( geom.isNull() )
3187 return QVariant();
3188
3189 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3190
3191 if ( idx < 0 )
3192 {
3193 //negative idx
3194 int count = geom.constGet()->nCoordinates();
3195 idx = count + idx;
3196 }
3197 else
3198 {
3199 //positive idx is 1 based
3200 idx -= 1;
3201 }
3202
3203 QgsVertexId vId;
3204 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3205 {
3206 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3207 return QVariant();
3208 }
3209
3210 QgsPoint point = geom.constGet()->vertexAt( vId );
3211 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3212}
3213
3214static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3215{
3216 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3217
3218 if ( geom.isNull() )
3219 return QVariant();
3220
3221 QgsVertexId vId;
3222 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3223 {
3224 return QVariant();
3225 }
3226
3227 QgsPoint point = geom.constGet()->vertexAt( vId );
3228 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3229}
3230
3231static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3232{
3233 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3234
3235 if ( geom.isNull() )
3236 return QVariant();
3237
3238 QgsVertexId vId;
3239 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3240 {
3241 return QVariant();
3242 }
3243
3244 QgsPoint point = geom.constGet()->vertexAt( vId );
3245 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3246}
3247
3248static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3249{
3250 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3251
3252 if ( geom.isNull() )
3253 return QVariant();
3254
3255 bool ignoreClosing = false;
3256 if ( values.length() > 1 )
3257 {
3258 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3259 }
3260
3261 QgsMultiPoint *mp = new QgsMultiPoint();
3262
3263 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3264 for ( const QgsRingSequence &part : sequence )
3265 {
3266 for ( const QgsPointSequence &ring : part )
3267 {
3268 bool skipLast = false;
3269 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3270 {
3271 skipLast = true;
3272 }
3273
3274 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3275 {
3276 mp->addGeometry( ring.at( i ).clone() );
3277 }
3278 }
3279 }
3280
3281 return QVariant::fromValue( QgsGeometry( mp ) );
3282}
3283
3284static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3285{
3286 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3287
3288 if ( geom.isNull() )
3289 return QVariant();
3290
3291 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3292
3293 //OK, now we have a complete list of segmentized lines from the geometry
3295 for ( QgsLineString *line : linesToProcess )
3296 {
3297 for ( int i = 0; i < line->numPoints() - 1; ++i )
3298 {
3300 segment->setPoints( QgsPointSequence()
3301 << line->pointN( i )
3302 << line->pointN( i + 1 ) );
3303 ml->addGeometry( segment );
3304 }
3305 delete line;
3306 }
3307
3308 return QVariant::fromValue( QgsGeometry( ml ) );
3309}
3310
3311static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3312{
3313 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3314
3315 if ( geom.isNull() )
3316 return QVariant();
3317
3318 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3319 if ( !curvePolygon && geom.isMultipart() )
3320 {
3321 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3322 {
3323 if ( collection->numGeometries() == 1 )
3324 {
3325 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3326 }
3327 }
3328 }
3329
3330 if ( !curvePolygon )
3331 return QVariant();
3332
3333 //idx is 1 based
3334 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3335
3336 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3337 return QVariant();
3338
3339 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3340 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3341 return result;
3342}
3343
3344static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3345{
3346 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3347
3348 if ( geom.isNull() )
3349 return QVariant();
3350
3351 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3352 if ( !collection )
3353 return QVariant();
3354
3355 //idx is 1 based
3356 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3357
3358 if ( idx < 0 || idx >= collection->numGeometries() )
3359 return QVariant();
3360
3361 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3362 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3363 return result;
3364}
3365
3366static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3367{
3368 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3369
3370 if ( geom.isNull() )
3371 return QVariant();
3372
3373 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3374 if ( !boundary )
3375 return QVariant();
3376
3377 return QVariant::fromValue( QgsGeometry( boundary ) );
3378}
3379
3380static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3381{
3382 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3383
3384 if ( geom.isNull() )
3385 return QVariant();
3386
3387 QgsGeometry merged = geom.mergeLines();
3388 if ( merged.isNull() )
3389 return QVariant();
3390
3391 return QVariant::fromValue( merged );
3392}
3393
3394static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3395{
3396 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3397 if ( geom.isNull() )
3398 return QVariant();
3399
3400 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3401 if ( geom2.isNull() )
3402 return QVariant();
3403
3404 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3405 if ( sharedPaths.isNull() )
3406 return QVariant();
3407
3408 return QVariant::fromValue( sharedPaths );
3409}
3410
3411
3412static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3413{
3414 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3415
3416 if ( geom.isNull() )
3417 return QVariant();
3418
3419 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3420
3421 QgsGeometry simplified = geom.simplify( tolerance );
3422 if ( simplified.isNull() )
3423 return QVariant();
3424
3425 return simplified;
3426}
3427
3428static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3429{
3430 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3431
3432 if ( geom.isNull() )
3433 return QVariant();
3434
3435 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3436
3438
3439 QgsGeometry simplified = simplifier.simplify( geom );
3440 if ( simplified.isNull() )
3441 return QVariant();
3442
3443 return simplified;
3444}
3445
3446static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3447{
3448 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3449
3450 if ( geom.isNull() )
3451 return QVariant();
3452
3453 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3454 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3455 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3456 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3457
3458 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3459 if ( smoothed.isNull() )
3460 return QVariant();
3461
3462 return smoothed;
3463}
3464
3465static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3466{
3467 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3468
3469 if ( geom.isNull() )
3470 return QVariant();
3471
3472 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3473 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3474 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3475
3476 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3477 if ( waved.isNull() )
3478 return QVariant();
3479
3480 return waved;
3481}
3482
3483static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3484{
3485 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3486
3487 if ( geom.isNull() )
3488 return QVariant();
3489
3490 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3491 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3492 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3493 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3494 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3495
3496 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3497 minAmplitude, maxAmplitude, seed );
3498 if ( waved.isNull() )
3499 return QVariant();
3500
3501 return waved;
3502}
3503
3504static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3505{
3506 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3507
3508 if ( geom.isNull() )
3509 return QVariant();
3510
3511 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3512 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3513 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3514
3515 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3516 if ( waved.isNull() )
3517 return QVariant();
3518
3519 return waved;
3520}
3521
3522static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3523{
3524 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3525
3526 if ( geom.isNull() )
3527 return QVariant();
3528
3529 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3530 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3531 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3532 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3533 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3534
3535 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3536 minAmplitude, maxAmplitude, seed );
3537 if ( waved.isNull() )
3538 return QVariant();
3539
3540 return waved;
3541}
3542
3543static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3544{
3545 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3546
3547 if ( geom.isNull() )
3548 return QVariant();
3549
3550 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3551 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3552 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3553
3554 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3555 if ( waved.isNull() )
3556 return QVariant();
3557
3558 return waved;
3559}
3560
3561static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3562{
3563 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3564
3565 if ( geom.isNull() )
3566 return QVariant();
3567
3568 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3569 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3570 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3571 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3572 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3573
3574 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3575 minAmplitude, maxAmplitude, seed );
3576 if ( waved.isNull() )
3577 return QVariant();
3578
3579 return waved;
3580}
3581
3582static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3583{
3584 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3585
3586 if ( geom.isNull() )
3587 return QVariant();
3588
3589 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3590 QVector< double > dashPattern;
3591 dashPattern.reserve( pattern.size() );
3592 for ( const QVariant &value : std::as_const( pattern ) )
3593 {
3594 bool ok = false;
3595 double v = value.toDouble( &ok );
3596 if ( ok )
3597 {
3598 dashPattern << v;
3599 }
3600 else
3601 {
3602 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3603 return QgsGeometry();
3604 }
3605 }
3606
3607 if ( dashPattern.size() % 2 != 0 )
3608 {
3609 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3610 return QgsGeometry();
3611 }
3612
3613 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3615 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3617 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3619 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3621 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3623 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3625 else
3626 {
3627 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3628 return QgsGeometry();
3629 }
3630
3631 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3633 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3635 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3637 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3639 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3641 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3643 else
3644 {
3645 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3646 return QgsGeometry();
3647 }
3648
3649 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3651 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3653 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3655 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3657 else
3658 {
3659 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3660 return QgsGeometry();
3661 }
3662
3663 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3664
3665 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3666 if ( result.isNull() )
3667 return QVariant();
3668
3669 return result;
3670}
3671
3672static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3673{
3674 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3675
3676 if ( geom.isNull() )
3677 return QVariant();
3678
3679 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3680 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3681 if ( densified.isNull() )
3682 return QVariant();
3683
3684 return densified;
3685}
3686
3687static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3688{
3689 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3690
3691 if ( geom.isNull() )
3692 return QVariant();
3693
3694 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3695 const QgsGeometry densified = geom.densifyByDistance( distance );
3696 if ( densified.isNull() )
3697 return QVariant();
3698
3699 return densified;
3700}
3701
3702static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3703{
3704 QVariantList list;
3705 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3706 {
3707 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3708 }
3709 else
3710 {
3711 list = values;
3712 }
3713
3714 QVector< QgsGeometry > parts;
3715 parts.reserve( list.size() );
3716 for ( const QVariant &value : std::as_const( list ) )
3717 {
3718 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
3719 if ( part.isNull() )
3720 return QgsGeometry();
3721 parts << part;
3722 }
3723
3724 return QgsGeometry::collectGeometry( parts );
3725}
3726
3727static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3728{
3729 if ( values.count() < 2 || values.count() > 4 )
3730 {
3731 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3732 return QVariant();
3733 }
3734
3735 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3736 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3737 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3738 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3739 switch ( values.count() )
3740 {
3741 case 2:
3742 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3743 case 3:
3744 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3745 case 4:
3746 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3747 }
3748 return QVariant(); //avoid warning
3749}
3750
3751static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3752{
3753 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3754 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3755 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3756 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3757}
3758
3759static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3760{
3761 if ( values.empty() )
3762 {
3763 return QVariant();
3764 }
3765
3766 QVector<QgsPoint> points;
3767 points.reserve( values.count() );
3768
3769 auto addPoint = [&points]( const QgsGeometry & geom )
3770 {
3771 if ( geom.isNull() )
3772 return;
3773
3774 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3775 return;
3776
3777 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3778 if ( !point )
3779 return;
3780
3781 points << *point;
3782 };
3783
3784 for ( const QVariant &value : values )
3785 {
3786 if ( value.userType() == QMetaType::Type::QVariantList )
3787 {
3788 const QVariantList list = value.toList();
3789 for ( const QVariant &v : list )
3790 {
3791 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3792 }
3793 }
3794 else
3795 {
3796 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3797 }
3798 }
3799
3800 if ( points.count() < 2 )
3801 return QVariant();
3802
3803 return QgsGeometry( new QgsLineString( points ) );
3804}
3805
3806static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3807{
3808 if ( values.count() < 1 )
3809 {
3810 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3811 return QVariant();
3812 }
3813
3814 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3815
3816 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3817 return outerRing; // if it's already a polygon we have nothing to do
3818
3819 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3820 return QVariant();
3821
3822 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
3823
3824 const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
3825 if ( !exteriorRing && outerRing.isMultipart() )
3826 {
3827 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3828 {
3829 if ( collection->numGeometries() == 1 )
3830 {
3831 exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3832 }
3833 }
3834 }
3835
3836 if ( !exteriorRing )
3837 return QVariant();
3838
3839 polygon->setExteriorRing( exteriorRing->segmentize() );
3840
3841
3842 for ( int i = 1; i < values.count(); ++i )
3843 {
3844 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3845 if ( ringGeom.isNull() )
3846 continue;
3847
3848 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3849 continue;
3850
3851 const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
3852 if ( !ring && ringGeom.isMultipart() )
3853 {
3854 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3855 {
3856 if ( collection->numGeometries() == 1 )
3857 {
3858 ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3859 }
3860 }
3861 }
3862
3863 if ( !ring )
3864 continue;
3865
3866 polygon->addInteriorRing( ring->segmentize() );
3867 }
3868
3869 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3870}
3871
3872static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3873{
3874 std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3875 std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3876 lineString->clear();
3877
3878 for ( const QVariant &value : values )
3879 {
3880 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3881 if ( geom.isNull() )
3882 return QVariant();
3883
3884 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3885 return QVariant();
3886
3887 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3888 if ( !point && geom.isMultipart() )
3889 {
3890 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3891 {
3892 if ( collection->numGeometries() == 1 )
3893 {
3894 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3895 }
3896 }
3897 }
3898
3899 if ( !point )
3900 return QVariant();
3901
3902 lineString->addVertex( *point );
3903 }
3904
3905 tr->setExteriorRing( lineString.release() );
3906
3907 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3908}
3909
3910static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3911{
3912 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3913 if ( geom.isNull() )
3914 return QVariant();
3915
3916 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3917 return QVariant();
3918
3919 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3920 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3921
3922 if ( segment < 3 )
3923 {
3924 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3925 return QVariant();
3926 }
3927 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3928 if ( !point && geom.isMultipart() )
3929 {
3930 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3931 {
3932 if ( collection->numGeometries() == 1 )
3933 {
3934 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3935 }
3936 }
3937 }
3938 if ( !point )
3939 return QVariant();
3940
3941 QgsCircle circ( *point, radius );
3942 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3943}
3944
3945static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3946{
3947 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3948 if ( geom.isNull() )
3949 return QVariant();
3950
3951 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3952 return QVariant();
3953
3954 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3955 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3956 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3957 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3958 if ( segment < 3 )
3959 {
3960 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3961 return QVariant();
3962 }
3963 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3964 if ( !point && geom.isMultipart() )
3965 {
3966 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3967 {
3968 if ( collection->numGeometries() == 1 )
3969 {
3970 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3971 }
3972 }
3973 }
3974 if ( !point )
3975 return QVariant();
3976
3977 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3978 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3979}
3980
3981static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3982{
3983
3984 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3985 if ( pt1.isNull() )
3986 return QVariant();
3987
3988 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
3989 return QVariant();
3990
3991 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3992 if ( pt2.isNull() )
3993 return QVariant();
3994
3995 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
3996 return QVariant();
3997
3998 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3999 if ( nbEdges < 3 )
4000 {
4001 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4002 return QVariant();
4003 }
4004
4005 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4007 {
4008 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4009 return QVariant();
4010 }
4011
4012 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
4013 if ( !center && pt1.isMultipart() )
4014 {
4015 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
4016 {
4017 if ( collection->numGeometries() == 1 )
4018 {
4019 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4020 }
4021 }
4022 }
4023 if ( !center )
4024 return QVariant();
4025
4026 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
4027 if ( !corner && pt2.isMultipart() )
4028 {
4029 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
4030 {
4031 if ( collection->numGeometries() == 1 )
4032 {
4033 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4034 }
4035 }
4036 }
4037 if ( !corner )
4038 return QVariant();
4039
4040 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4041
4042 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4043
4044}
4045
4046static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4047{
4048 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4049 if ( pt1.isNull() )
4050 return QVariant();
4051 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4052 return QVariant();
4053
4054 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4055 if ( pt2.isNull() )
4056 return QVariant();
4057 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4058 return QVariant();
4059
4060 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4061 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4062 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4063
4064 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4065}
4066
4067static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4068{
4069 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4070 if ( pt1.isNull() )
4071 return QVariant();
4072 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4073 return QVariant();
4074
4075 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4076 if ( pt2.isNull() )
4077 return QVariant();
4078 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4079 return QVariant();
4080
4081 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4082 if ( pt3.isNull() )
4083 return QVariant();
4084 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4085 return QVariant();
4086
4087 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4088 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4089 {
4090 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4091 return QVariant();
4092 }
4093 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4094 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4095 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4096 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4097 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4098}
4099
4100static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4101{
4102 if ( geom.isNull() )
4103 return QVariant();
4104
4105 if ( idx < 0 )
4106 {
4107 idx += geom.constGet()->nCoordinates();
4108 }
4109 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4110 {
4111 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4112 return QVariant();
4113 }
4114 return QVariant::fromValue( geom.vertexAt( idx ) );
4115}
4116
4117// function used for the old $ style
4118static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4119{
4120 FEAT_FROM_CONTEXT( context, feature )
4121 const QgsGeometry geom = feature.geometry();
4122 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4123
4124 const QVariant v = pointAt( geom, idx, parent );
4125
4126 if ( !v.isNull() )
4127 return QVariant( v.value<QgsPoint>().x() );
4128 else
4129 return QVariant();
4130}
4131static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4132{
4133 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))
4134 {
4135 return fcnOldXat( values, f, parent, node );
4136 }
4137 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)
4138 {
4139 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4140 }
4141
4142 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4143 if ( geom.isNull() )
4144 {
4145 return QVariant();
4146 }
4147
4148 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4149
4150 const QVariant v = pointAt( geom, vertexNumber, parent );
4151 if ( !v.isNull() )
4152 return QVariant( v.value<QgsPoint>().x() );
4153 else
4154 return QVariant();
4155}
4156
4157// function used for the old $ style
4158static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4159{
4160 FEAT_FROM_CONTEXT( context, feature )
4161 const QgsGeometry geom = feature.geometry();
4162 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4163
4164 const QVariant v = pointAt( geom, idx, parent );
4165
4166 if ( !v.isNull() )
4167 return QVariant( v.value<QgsPoint>().y() );
4168 else
4169 return QVariant();
4170}
4171static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4172{
4173 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))
4174 {
4175 return fcnOldYat( values, f, parent, node );
4176 }
4177 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)
4178 {
4179 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4180 }
4181
4182 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4183 if ( geom.isNull() )
4184 {
4185 return QVariant();
4186 }
4187
4188 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4189
4190 const QVariant v = pointAt( geom, vertexNumber, parent );
4191 if ( !v.isNull() )
4192 return QVariant( v.value<QgsPoint>().y() );
4193 else
4194 return QVariant();
4195}
4196
4197static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4198{
4199 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4200 if ( geom.isNull() )
4201 {
4202 return QVariant();
4203 }
4204
4205 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4206
4207 const QVariant v = pointAt( geom, vertexNumber, parent );
4208 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4209 return QVariant( v.value<QgsPoint>().z() );
4210 else
4211 return QVariant();
4212}
4213
4214static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4215{
4216 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4217 if ( geom.isNull() )
4218 {
4219 return QVariant();
4220 }
4221
4222 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4223
4224 const QVariant v = pointAt( geom, vertexNumber, parent );
4225 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4226 return QVariant( v.value<QgsPoint>().m() );
4227 else
4228 return QVariant();
4229}
4230
4231
4232static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4233{
4234 if ( !context )
4235 return QVariant();
4236
4237 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4238 if ( context->hasGeometry() )
4239 return context->geometry();
4240 else
4241 {
4242 FEAT_FROM_CONTEXT( context, f )
4243 QgsGeometry geom = f.geometry();
4244 if ( !geom.isNull() )
4245 return QVariant::fromValue( geom );
4246 else
4247 return QVariant();
4248 }
4249}
4250
4251static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4252{
4253 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4254 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4255 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4256 return result;
4257}
4258
4259static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4260{
4261 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4262 if ( wkb.isNull() )
4263 return QVariant();
4264
4265 QgsGeometry geom;
4266 geom.fromWkb( wkb );
4267 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4268}
4269
4270static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4271{
4272 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4273 QgsOgcUtils::Context ogcContext;
4274 if ( context )
4275 {
4276 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4277 if ( mapLayerPtr )
4278 {
4279 ogcContext.layer = mapLayerPtr.data();
4280 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4281 }
4282 }
4283 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4284 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4285 return result;
4286}
4287
4288static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4289{
4290 FEAT_FROM_CONTEXT( context, f )
4292 QgsDistanceArea *calc = parent->geomCalculator();
4293 if ( calc )
4294 {
4295 try
4296 {
4297 double area = calc->measureArea( f.geometry() );
4298 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4299 return QVariant( area );
4300 }
4301 catch ( QgsCsException & )
4302 {
4303 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4304 return QVariant();
4305 }
4306 }
4307 else
4308 {
4309 return QVariant( f.geometry().area() );
4310 }
4311}
4312
4313static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4314{
4315 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4316
4317 if ( geom.type() != Qgis::GeometryType::Polygon )
4318 return QVariant();
4319
4320 return QVariant( geom.area() );
4321}
4322
4323static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4324{
4325 FEAT_FROM_CONTEXT( context, f )
4327 QgsDistanceArea *calc = parent->geomCalculator();
4328 if ( calc )
4329 {
4330 try
4331 {
4332 double len = calc->measureLength( f.geometry() );
4333 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4334 return QVariant( len );
4335 }
4336 catch ( QgsCsException & )
4337 {
4338 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4339 return QVariant();
4340 }
4341 }
4342 else
4343 {
4344 return QVariant( f.geometry().length() );
4345 }
4346}
4347
4348static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4349{
4350 FEAT_FROM_CONTEXT( context, f )
4352 QgsDistanceArea *calc = parent->geomCalculator();
4353 if ( calc )
4354 {
4355 try
4356 {
4357 double len = calc->measurePerimeter( f.geometry() );
4358 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4359 return QVariant( len );
4360 }
4361 catch ( QgsCsException & )
4362 {
4363 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4364 return QVariant();
4365 }
4366 }
4367 else
4368 {
4369 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4370 }
4371}
4372
4373static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4374{
4375 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4376
4377 if ( geom.type() != Qgis::GeometryType::Polygon )
4378 return QVariant();
4379
4380 //length for polygons = perimeter
4381 return QVariant( geom.length() );
4382}
4383
4384static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4385{
4386 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4387 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4388}
4389
4390static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4391{
4392 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4393 if ( geom.isNull() )
4394 return QVariant();
4395
4396 return QVariant( geom.constGet()->partCount() );
4397}
4398
4399static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4400{
4401 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4402 if ( geom.isNull() )
4403 return QVariant();
4404
4405 return QVariant( geom.isMultipart() );
4406}
4407
4408static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4409{
4410 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4411
4412 if ( geom.isNull() )
4413 return QVariant();
4414
4415 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4416 if ( curvePolygon )
4417 return QVariant( curvePolygon->numInteriorRings() );
4418
4419 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4420 if ( collection )
4421 {
4422 //find first CurvePolygon in collection
4423 for ( int i = 0; i < collection->numGeometries(); ++i )
4424 {
4425 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4426 if ( !curvePolygon )
4427 continue;
4428
4429 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4430 }
4431 }
4432
4433 return QVariant();
4434}
4435
4436static QVariant fcnGeomNumRings( 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->ringCount() );
4446
4447 bool foundPoly = false;
4448 int ringCount = 0;
4449 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4450 if ( collection )
4451 {
4452 //find CurvePolygons in collection
4453 for ( int i = 0; i < collection->numGeometries(); ++i )
4454 {
4455 curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
4456 if ( !curvePolygon )
4457 continue;
4458
4459 foundPoly = true;
4460 ringCount += curvePolygon->ringCount();
4461 }
4462 }
4463
4464 if ( !foundPoly )
4465 return QVariant();
4466
4467 return QVariant( ringCount );
4468}
4469
4470static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4471{
4472 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4473 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4474 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4475 return result;
4476}
4477
4478static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4479{
4480 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4481 return QVariant::fromValue( geom.boundingBox().width() );
4482}
4483
4484static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4485{
4486 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4487 return QVariant::fromValue( geom.boundingBox().height() );
4488}
4489
4490static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4491{
4492 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4493 if ( geom.isNull() )
4494 return QVariant();
4495
4497}
4498
4499static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4500{
4501 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4502 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4503}
4504
4505static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4506{
4507 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4508 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4509}
4510
4511static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4512{
4513 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4514 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4515}
4516
4517static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4518{
4519 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4520 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4521}
4522
4523static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4524{
4525 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4526
4527 if ( geom.isNull() || geom.isEmpty( ) )
4528 return QVariant();
4529
4530 if ( !geom.constGet()->is3D() )
4531 return QVariant();
4532
4533 double max = std::numeric_limits< double >::lowest();
4534
4535 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4536 {
4537 double z = ( *it ).z();
4538
4539 if ( max < z )
4540 max = z;
4541 }
4542
4543 if ( max == std::numeric_limits< double >::lowest() )
4544 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4545
4546 return QVariant( max );
4547}
4548
4549static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4550{
4551 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4552
4553 if ( geom.isNull() || geom.isEmpty() )
4554 return QVariant();
4555
4556 if ( !geom.constGet()->is3D() )
4557 return QVariant();
4558
4559 double min = std::numeric_limits< double >::max();
4560
4561 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4562 {
4563 double z = ( *it ).z();
4564
4565 if ( z < min )
4566 min = z;
4567 }
4568
4569 if ( min == std::numeric_limits< double >::max() )
4570 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4571
4572 return QVariant( min );
4573}
4574
4575static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4576{
4577 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4578
4579 if ( geom.isNull() || geom.isEmpty() )
4580 return QVariant();
4581
4582 if ( !geom.constGet()->isMeasure() )
4583 return QVariant();
4584
4585 double min = std::numeric_limits< double >::max();
4586
4587 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4588 {
4589 double m = ( *it ).m();
4590
4591 if ( m < min )
4592 min = m;
4593 }
4594
4595 if ( min == std::numeric_limits< double >::max() )
4596 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4597
4598 return QVariant( min );
4599}
4600
4601static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4602{
4603 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4604
4605 if ( geom.isNull() || geom.isEmpty() )
4606 return QVariant();
4607
4608 if ( !geom.constGet()->isMeasure() )
4609 return QVariant();
4610
4611 double max = std::numeric_limits< double >::lowest();
4612
4613 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4614 {
4615 double m = ( *it ).m();
4616
4617 if ( max < m )
4618 max = m;
4619 }
4620
4621 if ( max == std::numeric_limits< double >::lowest() )
4622 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4623
4624 return QVariant( max );
4625}
4626
4627static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4628{
4629 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4630 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4631 if ( !curve )
4632 {
4633 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4634 return QVariant();
4635 }
4636
4637 return QVariant( curve->sinuosity() );
4638}
4639
4640static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4641{
4642 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4643 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4644 if ( !curve )
4645 {
4646 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4647 return QVariant();
4648 }
4649
4650 return QVariant( curve->straightDistance2d() );
4651}
4652
4653static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4654{
4655 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4656 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4657
4658 if ( !poly )
4659 {
4660 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4661 return QVariant();
4662 }
4663
4664 return QVariant( poly->roundness() );
4665}
4666
4667
4668
4669static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4670{
4671 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4672 if ( geom.isNull() )
4673 return QVariant();
4674
4675 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4676 flipped->swapXy();
4677 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4678}
4679
4680static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4681{
4682 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4683 if ( fGeom.isNull() )
4684 return QVariant();
4685
4686 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4687 if ( !curve && fGeom.isMultipart() )
4688 {
4689 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4690 {
4691 if ( collection->numGeometries() == 1 )
4692 {
4693 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4694 }
4695 }
4696 }
4697
4698 if ( !curve )
4699 return QVariant();
4700
4701 return QVariant::fromValue( curve->isClosed() );
4702}
4703
4704static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4705{
4706 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4707
4708 if ( geom.isNull() )
4709 return QVariant();
4710
4711 QVariant result;
4712 if ( !geom.isMultipart() )
4713 {
4714 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4715
4716 if ( !line )
4717 return QVariant();
4718
4719 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4720 closedLine->close();
4721
4722 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4723 }
4724 else
4725 {
4726 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4727
4728 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4729
4730 for ( int i = 0; i < collection->numGeometries(); ++i )
4731 {
4732 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4733 {
4734 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4735 closedLine->close();
4736
4737 closed->addGeometry( closedLine.release() );
4738 }
4739 }
4740 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4741 }
4742
4743 return result;
4744}
4745
4746static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4747{
4748 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4749 if ( fGeom.isNull() )
4750 return QVariant();
4751
4752 return QVariant::fromValue( fGeom.isEmpty() );
4753}
4754
4755static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4756{
4757 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4758 return QVariant::fromValue( true );
4759
4760 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4761 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4762}
4763
4764static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4765{
4766 if ( values.length() < 2 || values.length() > 3 )
4767 return QVariant();
4768
4769 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4770 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4771
4772 if ( fGeom.isNull() || sGeom.isNull() )
4773 return QVariant();
4774
4775 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4776
4777 if ( values.length() == 2 )
4778 {
4779 //two geometry arguments, return relation
4780 QString result = engine->relate( sGeom.constGet() );
4781 return QVariant::fromValue( result );
4782 }
4783 else
4784 {
4785 //three arguments, test pattern
4786 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4787 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4788 return QVariant::fromValue( result );
4789 }
4790}
4791
4792static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4793{
4794 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4795 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4796 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4797}
4798static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4799{
4800 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4801 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4802 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4803}
4804static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4805{
4806 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4807 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4808 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4809}
4810static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4811{
4812 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4813 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4814 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4815}
4816static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4817{
4818 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4819 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4820 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4821}
4822static QVariant fcnContains( 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.contains( sGeom ) ? TVL_True : TVL_False;
4827}
4828static QVariant fcnOverlaps( 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.overlaps( sGeom ) ? TVL_True : TVL_False;
4833}
4834static QVariant fcnWithin( 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.within( sGeom ) ? TVL_True : TVL_False;
4839}
4840
4841static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4842{
4843 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4844 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4845 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4846 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4847 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4848 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4849
4851 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4852 capStyle = Qgis::EndCapStyle::Flat;
4853 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4854 capStyle = Qgis::EndCapStyle::Square;
4855
4857 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4858 joinStyle = Qgis::JoinStyle::Miter;
4859 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4860 joinStyle = Qgis::JoinStyle::Bevel;
4861
4862 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4863 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4864 return result;
4865}
4866
4867static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4868{
4869 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4870 const QgsGeometry reoriented = fGeom.forceRHR();
4871 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4872}
4873
4874static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4875{
4876 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4877 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4878 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4879}
4880
4881static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4882{
4883 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4884 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4885 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4886}
4887
4888static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4889{
4890 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4891 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4892 if ( !pt && fGeom.isMultipart() )
4893 {
4894 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4895 {
4896 if ( collection->numGeometries() == 1 )
4897 {
4898 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4899 }
4900 }
4901 }
4902
4903 if ( !pt )
4904 {
4905 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4906 return QVariant();
4907 }
4908
4909 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4910 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4911 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4912 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4913
4914 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4915 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4916 return result;
4917}
4918
4919static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4920{
4921 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4922 if ( fGeom.type() != Qgis::GeometryType::Line )
4923 {
4924 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4925 return QVariant();
4926 }
4927
4928 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4929 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4930 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4931
4932 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4933 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4934 return result;
4935}
4936
4937static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4938{
4939 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4940 if ( fGeom.type() != Qgis::GeometryType::Line )
4941 {
4942 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4943 return QVariant();
4944 }
4945
4946 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4947
4948 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4949 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4950 return result;
4951}
4952
4953static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4954{
4955 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4956 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4957 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4958 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4959 if ( joinInt < 1 || joinInt > 3 )
4960 return QVariant();
4961 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4962
4963 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4964
4965 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4966 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4967 return result;
4968}
4969
4970static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4971{
4972 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4973 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4974 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4975
4976 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4977 if ( joinInt < 1 || joinInt > 3 )
4978 return QVariant();
4979 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4980
4981 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4982
4983 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
4984 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4985 return result;
4986}
4987
4988static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4989{
4990 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4991 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4992 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4993
4994 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
4995 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4996 return result;
4997}
4998
4999static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5000{
5001 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5002 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5003 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5004 fGeom.translate( dx, dy );
5005 return QVariant::fromValue( fGeom );
5006}
5007
5008static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5009{
5010 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5011 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5012 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5013 : QgsGeometry();
5014 const bool perPart = values.value( 3 ).toBool();
5015
5016 if ( center.isNull() && perPart && fGeom.isMultipart() )
5017 {
5018 // no explicit center, rotating per part
5019 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5020 // the result is equivalent to setting perPart as false anyway)
5021 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5022 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5023 {
5024 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5025 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5026 t.rotate( -rotation );
5027 t.translate( -partCenter.x(), -partCenter.y() );
5028 ( *it )->transform( t );
5029 }
5030 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5031 }
5032 else
5033 {
5034 QgsPointXY pt;
5035 if ( center.isEmpty() )
5036 {
5037 // if center wasn't specified, use bounding box centroid
5038 pt = fGeom.boundingBox().center();
5039 }
5041 {
5042 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5043 return QVariant();
5044 }
5045 else
5046 {
5047 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
5048 }
5049
5050 fGeom.rotate( rotation, pt );
5051 return QVariant::fromValue( fGeom );
5052 }
5053}
5054
5055static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5056{
5057 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5058 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5059 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5060 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5061 : QgsGeometry();
5062
5063 QgsPointXY pt;
5064 if ( center.isNull() )
5065 {
5066 // if center wasn't specified, use bounding box centroid
5067 pt = fGeom.boundingBox().center();
5068 }
5070 {
5071 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5072 return QVariant();
5073 }
5074 else
5075 {
5076 pt = center.asPoint();
5077 }
5078
5079 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5080 t.scale( xScale, yScale );
5081 t.translate( -pt.x(), -pt.y() );
5082 fGeom.transform( t );
5083 return QVariant::fromValue( fGeom );
5084}
5085
5086static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5087{
5088 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5089 if ( fGeom.isNull() )
5090 {
5091 return QVariant();
5092 }
5093
5094 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5095 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5096
5097 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5098
5099 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5100 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5101
5102 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5103 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5104 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5105 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5106
5107 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5108 {
5109 fGeom.get()->addZValue( 0 );
5110 }
5111 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5112 {
5113 fGeom.get()->addMValue( 0 );
5114 }
5115
5116 QTransform transform;
5117 transform.translate( deltaX, deltaY );
5118 transform.rotate( rotationZ );
5119 transform.scale( scaleX, scaleY );
5120 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5121
5122 return QVariant::fromValue( fGeom );
5123}
5124
5125
5126static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5127{
5128 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5129 QgsGeometry geom = fGeom.centroid();
5130 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5131 return result;
5132}
5133static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5134{
5135 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5136 QgsGeometry geom = fGeom.pointOnSurface();
5137 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5138 return result;
5139}
5140
5141static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5142{
5143 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5144 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5145 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5146 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5147 return result;
5148}
5149
5150static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5151{
5152 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5153 QgsGeometry geom = fGeom.convexHull();
5154 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5155 return result;
5156}
5157
5158#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5159static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5160{
5161 try
5162 {
5163 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5164 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5165 const bool allowHoles = values.value( 2 ).toBool();
5166 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5167 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5168 return result;
5169 }
5170 catch ( QgsCsException &cse )
5171 {
5172 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5173 return QVariant();
5174 }
5175}
5176#endif
5177
5178static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5179{
5180 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5181 int segments = 36;
5182 if ( values.length() == 2 )
5183 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5184 if ( segments < 0 )
5185 {
5186 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5187 return QVariant();
5188 }
5189
5190 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5191 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5192 return result;
5193}
5194
5195static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5196{
5197 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5199 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5200 return result;
5201}
5202
5203static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5204{
5205 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5206
5207 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5208 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5209 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5210
5211 double area, angle, width, height;
5212 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5213
5214 if ( geom.isNull() )
5215 {
5216 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5217 return QVariant();
5218 }
5219 return angle;
5220}
5221
5222static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5223{
5224 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5225 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5226 QgsGeometry geom = fGeom.difference( sGeom );
5227 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5228 return result;
5229}
5230
5231static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5232{
5233 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5234 if ( fGeom.isNull() )
5235 return QVariant();
5236
5237 QVariant result;
5238 if ( !fGeom.isMultipart() )
5239 {
5240 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5241 if ( !curve )
5242 return QVariant();
5243
5244 QgsCurve *reversed = curve->reversed();
5245 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5246 }
5247 else
5248 {
5249 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5250 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5251 for ( int i = 0; i < collection->numGeometries(); ++i )
5252 {
5253 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5254 {
5255 reversed->addGeometry( curve->reversed() );
5256 }
5257 else
5258 {
5259 reversed->addGeometry( collection->geometryN( i )->clone() );
5260 }
5261 }
5262 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5263 }
5264 return result;
5265}
5266
5267static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5268{
5269 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5270 if ( fGeom.isNull() )
5271 return QVariant();
5272
5273 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5274 if ( !curvePolygon && fGeom.isMultipart() )
5275 {
5276 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5277 {
5278 if ( collection->numGeometries() == 1 )
5279 {
5280 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5281 }
5282 }
5283 }
5284
5285 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5286 return QVariant();
5287
5288 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5289 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5290 return result;
5291}
5292
5293static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5294{
5295 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5296 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5297 return QVariant( fGeom.distance( sGeom ) );
5298}
5299
5300static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5301{
5302 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5303 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5304
5305 double res = -1;
5306 if ( values.length() == 3 && values.at( 2 ).isValid() )
5307 {
5308 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5309 densify = std::clamp( densify, 0.0, 1.0 );
5310 res = g1.hausdorffDistanceDensify( g2, densify );
5311 }
5312 else
5313 {
5314 res = g1.hausdorffDistance( g2 );
5315 }
5316
5317 return res > -1 ? QVariant( res ) : QVariant();
5318}
5319
5320static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5321{
5322 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5323 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5324 QgsGeometry geom = fGeom.intersection( sGeom );
5325 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5326 return result;
5327}
5328static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5329{
5330 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5331 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5332 QgsGeometry geom = fGeom.symDifference( sGeom );
5333 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5334 return result;
5335}
5336static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5337{
5338 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5339 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5340 QgsGeometry geom = fGeom.combine( sGeom );
5341 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5342 return result;
5343}
5344
5345static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5346{
5347 if ( values.length() < 1 || values.length() > 2 )
5348 return QVariant();
5349
5350 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5351 int prec = 8;
5352 if ( values.length() == 2 )
5353 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5354 QString wkt = fGeom.asWkt( prec );
5355 return QVariant( wkt );
5356}
5357
5358static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5359{
5360 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5361 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5362}
5363
5364static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5365{
5366 if ( values.length() != 2 )
5367 {
5368 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5369 return QVariant();
5370 }
5371
5372 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5373 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5374
5375 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5376 if ( !pt1 && fGeom1.isMultipart() )
5377 {
5378 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5379 {
5380 if ( collection->numGeometries() == 1 )
5381 {
5382 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5383 }
5384 }
5385 }
5386
5387 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5388 if ( !pt2 && fGeom2.isMultipart() )
5389 {
5390 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5391 {
5392 if ( collection->numGeometries() == 1 )
5393 {
5394 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5395 }
5396 }
5397 }
5398
5399 if ( !pt1 || !pt2 )
5400 {
5401 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5402 return QVariant();
5403 }
5404
5405 // Code from PostGIS
5406 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5407 {
5408 if ( pt1->y() < pt2->y() )
5409 return 0.0;
5410 else if ( pt1->y() > pt2->y() )
5411 return M_PI;
5412 else
5413 return 0;
5414 }
5415
5416 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5417 {
5418 if ( pt1->x() < pt2->x() )
5419 return M_PI_2;
5420 else if ( pt1->x() > pt2->x() )
5421 return M_PI + ( M_PI_2 );
5422 else
5423 return 0;
5424 }
5425
5426 if ( pt1->x() < pt2->x() )
5427 {
5428 if ( pt1->y() < pt2->y() )
5429 {
5430 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5431 }
5432 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5433 {
5434 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5435 + ( M_PI_2 );
5436 }
5437 }
5438
5439 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5440 {
5441 if ( pt1->y() > pt2->y() )
5442 {
5443 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5444 + M_PI;
5445 }
5446 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5447 {
5448 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5449 + ( M_PI + ( M_PI_2 ) );
5450 }
5451 }
5452}
5453
5454static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5455{
5456 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5457 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5458 QString sourceCrs = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5459 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5460
5461 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5462 {
5463 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5464 return QVariant();
5465 }
5466
5467 const QgsPointXY point1 = geom1.asPoint();
5468 const QgsPointXY point2 = geom2.asPoint();
5469 if ( point1.isEmpty() || point2.isEmpty() )
5470 {
5471 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5472 return QVariant();
5473 }
5474
5476 if ( context )
5477 {
5478 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5479
5480 if ( sourceCrs.isEmpty() )
5481 {
5482 sourceCrs = context->variable( QStringLiteral( "layer_crs" ) ).toString();
5483 }
5484
5485 if ( ellipsoid.isEmpty() )
5486 {
5487 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5488 }
5489 }
5490
5492 if ( !sCrs.isValid() )
5493 {
5494 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5495 return QVariant();
5496 }
5497
5498 QgsDistanceArea da;
5499 da.setSourceCrs( sCrs, tContext );
5500 if ( !da.setEllipsoid( ellipsoid ) )
5501 {
5502 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5503 return QVariant();
5504 }
5505
5506 try
5507 {
5508 const double bearing = da.bearing( point1, point2 );
5509 if ( std::isfinite( bearing ) )
5510 {
5511 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5512 }
5513 }
5514 catch ( QgsCsException &cse )
5515 {
5516 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5517 return QVariant();
5518 }
5519 return QVariant();
5520}
5521
5522static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5523{
5524 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5525
5527 {
5528 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5529 return QVariant();
5530 }
5531
5532 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5533 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5534 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5535
5536 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5537 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5538
5539 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5540}
5541
5542static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5543{
5544 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5545 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5546
5547 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5548 if ( !pt1 && fGeom1.isMultipart() )
5549 {
5550 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5551 {
5552 if ( collection->numGeometries() == 1 )
5553 {
5554 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5555 }
5556 }
5557 }
5558 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5559 if ( !pt2 && fGeom2.isMultipart() )
5560 {
5561 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5562 {
5563 if ( collection->numGeometries() == 1 )
5564 {
5565 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5566 }
5567 }
5568 }
5569
5570 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5571 !pt1 || !pt2 )
5572 {
5573 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5574 return QVariant();
5575 }
5576
5577 return pt1->inclination( *pt2 );
5578
5579}
5580
5581static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5582{
5583 if ( values.length() != 3 )
5584 return QVariant();
5585
5586 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5587 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5588 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5589
5590 QgsGeometry geom = fGeom.extrude( x, y );
5591
5592 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5593 return result;
5594}
5595
5596static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5597{
5598 if ( values.length() < 2 )
5599 return QVariant();
5600
5601 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5602
5603 if ( !fGeom.isMultipart() )
5604 return values.at( 0 );
5605
5606 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5607 QVariant cachedExpression;
5608 if ( ctx )
5609 cachedExpression = ctx->cachedValue( expString );
5610 QgsExpression expression;
5611
5612 if ( cachedExpression.isValid() )
5613 {
5614 expression = cachedExpression.value<QgsExpression>();
5615 }
5616 else
5617 expression = QgsExpression( expString );
5618
5619 bool asc = values.value( 2 ).toBool();
5620
5621 QgsExpressionContext *unconstedContext = nullptr;
5622 QgsFeature f;
5623 if ( ctx )
5624 {
5625 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5626 // so no reason to worry
5627 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5628 f = ctx->feature();
5629 }
5630 else
5631 {
5632 // If there's no context provided, create a fake one
5633 unconstedContext = new QgsExpressionContext();
5634 }
5635
5636 const QgsGeometryCollection *collection = qgsgeometry_cast<const QgsGeometryCollection *>( fGeom.constGet() );
5637 Q_ASSERT( collection ); // Should have failed the multipart check above
5638
5640 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5641 QgsExpressionSorter sorter( orderBy );
5642
5643 QList<QgsFeature> partFeatures;
5644 partFeatures.reserve( collection->partCount() );
5645 for ( int i = 0; i < collection->partCount(); ++i )
5646 {
5647 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5648 partFeatures << f;
5649 }
5650
5651 sorter.sortFeatures( partFeatures, unconstedContext );
5652
5653 QgsGeometryCollection *orderedGeom = qgsgeometry_cast<QgsGeometryCollection *>( fGeom.constGet()->clone() );
5654
5655 Q_ASSERT( orderedGeom );
5656
5657 while ( orderedGeom->partCount() )
5658 orderedGeom->removeGeometry( 0 );
5659
5660 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5661 {
5662 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5663 }
5664
5665 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5666
5667 if ( !ctx )
5668 delete unconstedContext;
5669
5670 return result;
5671}
5672
5673static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5674{
5675 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5676 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5677
5678 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5679
5680 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5681 return result;
5682}
5683
5684static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5685{
5686 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5687 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5688
5689 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5690
5691 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5692 return result;
5693}
5694
5695static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5696{
5697 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5698 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5699
5700 QgsGeometry geom = lineGeom.interpolate( distance );
5701
5702 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5703 return result;
5704}
5705
5706static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5707{
5708 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5709 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5710 const bool use3DDistance = values.at( 2 ).toBool();
5711
5712 double x, y, z, distance;
5713
5714 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5715 if ( !line )
5716 {
5717 return QVariant();
5718 }
5719
5720 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
5721 {
5722 QgsPoint point( x, y );
5723 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
5724 {
5725 point.addZValue( z );
5726 }
5727 return QVariant::fromValue( QgsGeometry( point.clone() ) );
5728 }
5729
5730 return QVariant();
5731}
5732
5733static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5734{
5735 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5736 if ( lineGeom.type() != Qgis::GeometryType::Line )
5737 {
5738 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5739 return QVariant();
5740 }
5741
5742 const QgsCurve *curve = nullptr;
5743 if ( !lineGeom.isMultipart() )
5744 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5745 else
5746 {
5747 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( lineGeom.constGet() ) )
5748 {
5749 if ( collection->numGeometries() > 0 )
5750 {
5751 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5752 }
5753 }
5754 }
5755 if ( !curve )
5756 return QVariant();
5757
5758 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5759 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5760
5761 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5762 QgsGeometry result( std::move( substring ) );
5763 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5764}
5765
5766static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5767{
5768 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5769 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5770
5771 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5772}
5773
5774static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5775{
5776 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5777 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5778 if ( vertex < 0 )
5779 {
5780 //negative idx
5781 int count = geom.constGet()->nCoordinates();
5782 vertex = count + vertex;
5783 }
5784
5785 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5786}
5787
5788static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5789{
5790 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5791 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5792 if ( vertex < 0 )
5793 {
5794 //negative idx
5795 int count = geom.constGet()->nCoordinates();
5796 vertex = count + vertex;
5797 }
5798
5799 return geom.distanceToVertex( vertex );
5800}
5801
5802static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5803{
5804 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5805 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5806
5807 double distance = lineGeom.lineLocatePoint( pointGeom );
5808
5809 return distance >= 0 ? distance : QVariant();
5810}
5811
5812static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5813{
5814 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5815 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5816 const bool use3DDistance = values.at( 2 ).toBool();
5817
5818 double x, y, z, distance;
5819
5820 const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( lineGeom.constGet() );
5821 if ( !line )
5822 {
5823 return QVariant();
5824 }
5825
5826 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
5827 return found ? distance : QVariant();
5828}
5829
5830static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5831{
5832 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
5833 {
5834 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5835 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5836 }
5837
5838 if ( values.length() >= 1 )
5839 {
5840 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5841 return QVariant( qlonglong( std::round( number ) ) );
5842 }
5843
5844 return QVariant();
5845}
5846
5847static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5848{
5849 Q_UNUSED( values )
5850 Q_UNUSED( parent )
5851 return M_PI;
5852}
5853
5854static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5855{
5856 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
5857 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5858 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5859 if ( places < 0 )
5860 {
5861 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
5862 return QVariant();
5863 }
5864
5865 const bool omitGroupSeparator = values.value( 3 ).toBool();
5866 const bool trimTrailingZeros = values.value( 4 ).toBool();
5867
5868 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5869 if ( !omitGroupSeparator )
5870 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
5871 else
5872 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
5873
5874 QString res = locale.toString( value, 'f', places );
5875
5876 if ( trimTrailingZeros )
5877 {
5878#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
5879 const QChar decimal = locale.decimalPoint();
5880 const QChar zeroDigit = locale.zeroDigit();
5881#else
5882 const QChar decimal = locale.decimalPoint().at( 0 );
5883 const QChar zeroDigit = locale.zeroDigit().at( 0 );
5884#endif
5885
5886 if ( res.contains( decimal ) )
5887 {
5888 int trimPoint = res.length() - 1;
5889
5890 while ( res.at( trimPoint ) == zeroDigit )
5891 trimPoint--;
5892
5893 if ( res.at( trimPoint ) == decimal )
5894 trimPoint--;
5895
5896 res.truncate( trimPoint + 1 );
5897 }
5898 }
5899
5900 return res;
5901}
5902
5903static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5904{
5905 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
5906 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5907 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5908
5909 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
5910 if ( format.indexOf( "Z" ) > 0 )
5911 datetime = datetime.toUTC();
5912
5913 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
5914 return locale.toString( datetime, format );
5915}
5916
5917static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5918{
5919 const QVariant variant = values.at( 0 );
5920 bool isQColor;
5921 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
5922 if ( !color.isValid() )
5923 return QVariant();
5924
5925 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5926 if ( color.spec() == QColor::Spec::Cmyk )
5927 {
5928 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
5929 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
5930 }
5931 else
5932 {
5933 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
5934 color.setRgbF( avg, avg, avg, alpha );
5935 }
5936
5937 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
5938}
5939
5940static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5941{
5942 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5943 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
5944 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5945 if ( ratio > 1 )
5946 {
5947 ratio = 1;
5948 }
5949 else if ( ratio < 0 )
5950 {
5951 ratio = 0;
5952 }
5953
5954 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
5955 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
5956 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
5957 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
5958
5959 QColor newColor( red, green, blue, alpha );
5960
5961 return QgsSymbolLayerUtils::encodeColor( newColor );
5962}
5963
5964static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5965{
5966 const QVariant variant1 = values.at( 0 );
5967 const QVariant variant2 = values.at( 1 );
5968
5969 if ( variant1.userType() != variant2.userType() )
5970 {
5971 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
5972 return QVariant();
5973 }
5974
5975 bool isQColor;
5976 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
5977 if ( !color1.isValid() )
5978 return QVariant();
5979
5980 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
5981 if ( !color2.isValid() )
5982 return QVariant();
5983
5984 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
5985 {
5986 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
5987 return QVariant();
5988 }
5989
5990 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
5991
5992 // TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
5993 // NOLINTBEGIN(bugprone-narrowing-conversions)
5994
5995 QColor newColor;
5996 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
5997 if ( color1.spec() == QColor::Spec::Cmyk )
5998 {
5999 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6000 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6001 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6002 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6003 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6004 }
6005 else
6006 {
6007 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6008 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6009 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6010 newColor = QColor::fromRgbF( red, green, blue, alpha );
6011 }
6012
6013 // NOLINTEND(bugprone-narrowing-conversions)
6014
6015 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6016}
6017
6018static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6019{
6020 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6021 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6022 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6023 QColor color = QColor( red, green, blue );
6024 if ( ! color.isValid() )
6025 {
6026 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6027 color = QColor( 0, 0, 0 );
6028 }
6029
6030 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6031}
6032
6033static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6034{
6035 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6036 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6037 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6038 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6039 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6040 if ( ! color.isValid() )
6041 {
6042 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6043 return QVariant();
6044 }
6045
6046 return color;
6047}
6048
6049static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6050{
6051 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6052 QVariant value = node->eval( parent, context );
6053 if ( parent->hasEvalError() )
6054 {
6055 parent->setEvalErrorString( QString() );
6056 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6058 value = node->eval( parent, context );
6060 }
6061 return value;
6062}
6063
6064static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6065{
6066 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6068 QVariant value = node->eval( parent, context );
6070 if ( value.toBool() )
6071 {
6072 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6074 value = node->eval( parent, context );
6076 }
6077 else
6078 {
6079 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6081 value = node->eval( parent, context );
6083 }
6084 return value;
6085}
6086
6087static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6088{
6089 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6090 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6091 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6092 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6093 QColor color = QColor( red, green, blue, alpha );
6094 if ( ! color.isValid() )
6095 {
6096 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6097 color = QColor( 0, 0, 0 );
6098 }
6099 return QgsSymbolLayerUtils::encodeColor( color );
6100}
6101
6102QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6103{
6104 QgsGradientColorRamp expRamp;
6105 const QgsColorRamp *ramp = nullptr;
6106 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6107 {
6108 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6109 ramp = &expRamp;
6110 }
6111 else
6112 {
6113 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6114 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6115 if ( ! ramp )
6117 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6118 return QVariant();
6119 }
6120 }
6121
6122 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6123 QColor color = ramp->color( value );
6124 return color;
6125}
6126
6127QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6128{
6129 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6130 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6131}
6132
6133static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6134{
6135 // Hue ranges from 0 - 360
6136 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6137 // Saturation ranges from 0 - 100
6138 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6139 // Lightness ranges from 0 - 100
6140 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6141
6142 QColor color = QColor::fromHslF( hue, saturation, lightness );
6143
6144 if ( ! color.isValid() )
6145 {
6146 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6147 color = QColor( 0, 0, 0 );
6148 }
6149
6150 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6151}
6152
6153static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6154{
6155 // Hue ranges from 0 - 360
6156 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6157 // Saturation ranges from 0 - 100
6158 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6159 // Lightness ranges from 0 - 100
6160 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6161 // Alpha ranges from 0 - 255
6162 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6163
6164 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6165 if ( ! color.isValid() )
6166 {
6167 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6168 color = QColor( 0, 0, 0 );
6169 }
6170 return QgsSymbolLayerUtils::encodeColor( color );
6171}
6172
6173static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6174{
6175 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6176 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6177 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6178 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6179
6180 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6181 if ( ! color.isValid() )
6182 {
6183 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6184 return QVariant();
6185 }
6186
6187 return color;
6188}
6189
6190static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6191{
6192 // Hue ranges from 0 - 360
6193 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6194 // Saturation ranges from 0 - 100
6195 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6196 // Value ranges from 0 - 100
6197 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6198
6199 QColor color = QColor::fromHsvF( hue, saturation, value );
6200
6201 if ( ! color.isValid() )
6202 {
6203 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6204 color = QColor( 0, 0, 0 );
6205 }
6206
6207 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6208}
6209
6210static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6211{
6212 // Hue ranges from 0 - 360
6213 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6214 // Saturation ranges from 0 - 100
6215 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6216 // Value ranges from 0 - 100
6217 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6218 // Alpha ranges from 0 - 255
6219 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6220
6221 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6222 if ( ! color.isValid() )
6223 {
6224 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6225 color = QColor( 0, 0, 0 );
6226 }
6227 return QgsSymbolLayerUtils::encodeColor( color );
6228}
6229
6230static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6231{
6232 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6233 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6234 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6235 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6236 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6237
6238 if ( ! color.isValid() )
6239 {
6240 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6241 return QVariant();
6242 }
6243
6244 return color;
6245}
6246
6247static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6248{
6249 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6250 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6251 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6252 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6253 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6254
6255 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6256 if ( ! color.isValid() )
6257 {
6258 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6259 return QVariant();
6260 }
6261
6262 return color;
6263}
6264
6265static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6266{
6267 // Cyan ranges from 0 - 100
6268 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6269 // Magenta ranges from 0 - 100
6270 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6271 // Yellow ranges from 0 - 100
6272 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6273 // Black ranges from 0 - 100
6274 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6275
6276 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6277
6278 if ( ! color.isValid() )
6279 {
6280 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6281 color = QColor( 0, 0, 0 );
6282 }
6283
6284 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6285}
6286
6287static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6288{
6289 // Cyan ranges from 0 - 100
6290 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6291 // Magenta ranges from 0 - 100
6292 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6293 // Yellow ranges from 0 - 100
6294 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6295 // Black ranges from 0 - 100
6296 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6297 // Alpha ranges from 0 - 255
6298 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6299
6300 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6301 if ( ! color.isValid() )
6302 {
6303 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6304 color = QColor( 0, 0, 0 );
6305 }
6306 return QgsSymbolLayerUtils::encodeColor( color );
6307}
6308
6309static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6310{
6311 const QVariant variant = values.at( 0 );
6312 bool isQColor;
6313 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6314 if ( !color.isValid() )
6315 return QVariant();
6316
6317 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6318 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6319 return color.red();
6320 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6321 return color.green();
6322 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6323 return color.blue();
6324 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6325 return color.alpha();
6326 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6327 return static_cast< double >( color.hsvHueF() * 360 );
6328 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6329 return static_cast< double >( color.hsvSaturationF() * 100 );
6330 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6331 return static_cast< double >( color.valueF() * 100 );
6332 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6333 return static_cast< double >( color.hslHueF() * 360 );
6334 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6335 return static_cast< double >( color.hslSaturationF() * 100 );
6336 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6337 return static_cast< double >( color.lightnessF() * 100 );
6338 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6339 return static_cast< double >( color.cyanF() * 100 );
6340 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6341 return static_cast< double >( color.magentaF() * 100 );
6342 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6343 return static_cast< double >( color.yellowF() * 100 );
6344 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6345 return static_cast< double >( color.blackF() * 100 );
6346
6347 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6348 return QVariant();
6349}
6350
6351static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6352{
6353 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6354 if ( map.empty() )
6355 {
6356 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6357 return QVariant();
6358 }
6359
6360 QList< QColor > colors;
6362 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6363 {
6364 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6365 if ( !colors.last().isValid() )
6366 {
6367 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6368 return QVariant();
6369 }
6370
6371 double step = it.key().toDouble();
6372 if ( it == map.constBegin() )
6373 {
6374 if ( step != 0.0 )
6375 stops << QgsGradientStop( step, colors.last() );
6376 }
6377 else if ( it == map.constEnd() )
6378 {
6379 if ( step != 1.0 )
6380 stops << QgsGradientStop( step, colors.last() );
6381 }
6382 else
6383 {
6384 stops << QgsGradientStop( step, colors.last() );
6385 }
6386 }
6387 bool discrete = values.at( 1 ).toBool();
6388
6389 if ( colors.empty() )
6390 return QVariant();
6391
6392 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6393}
6394
6395static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6396{
6397 const QVariant variant = values.at( 0 );
6398 bool isQColor;
6399 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6400 if ( !color.isValid() )
6401 return QVariant();
6402
6403 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6404 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6405 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6406 color.setRed( std::clamp( value, 0, 255 ) );
6407 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6408 color.setGreen( std::clamp( value, 0, 255 ) );
6409 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6410 color.setBlue( std::clamp( value, 0, 255 ) );
6411 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6412 color.setAlpha( std::clamp( value, 0, 255 ) );
6413 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6414 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6415 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6416 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6417 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6418 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6419 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6420 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6421 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6422 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6423 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6424 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6425 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6426 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6427 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6428 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6429 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6430 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6431 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6432 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6433 else
6434 {
6435 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6436 return QVariant();
6437 }
6438 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6439}
6440
6441static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6442{
6443 const QVariant variant = values.at( 0 );
6444 bool isQColor;
6445 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6446 if ( !color.isValid() )
6447 return QVariant();
6448
6449 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6450
6451 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6452}
6453
6454static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6455{
6456 const QVariant variant = values.at( 0 );
6457 bool isQColor;
6458 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6459 if ( !color.isValid() )
6460 return QVariant();
6461
6462 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6463
6464 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6465}
6466
6467static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6468{
6469 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6470 QgsGeometry geom = feat.geometry();
6471 if ( !geom.isNull() )
6472 return QVariant::fromValue( geom );
6473 return QVariant();
6474}
6475
6476static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6477{
6478 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6479 if ( !feat.isValid() )
6480 return QVariant();
6481 return feat.id();
6482}
6483
6484static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6485{
6486 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6487 QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6488 QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6489
6491 if ( ! s.isValid() )
6492 return QVariant::fromValue( fGeom );
6494 if ( ! d.isValid() )
6495 return QVariant::fromValue( fGeom );
6496
6498 if ( context )
6499 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6500 QgsCoordinateTransform t( s, d, tContext );
6501 try
6502 {
6504 return QVariant::fromValue( fGeom );
6505 }
6506 catch ( QgsCsException &cse )
6507 {
6508 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6509 return QVariant();
6510 }
6511 return QVariant();
6512}
6513
6514
6515static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6516{
6517 bool foundLayer = false;
6518 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6519
6520 //no layer found
6521 if ( !featureSource || !foundLayer )
6522 {
6523 return QVariant();
6524 }
6525
6526 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6527
6529 req.setFilterFid( fid );
6530 req.setTimeout( 10000 );
6531 req.setRequestMayBeNested( true );
6532 if ( context )
6533 req.setFeedback( context->feedback() );
6534 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6535
6536 QgsFeature fet;
6537 QVariant result;
6538 if ( fIt.nextFeature( fet ) )
6539 result = QVariant::fromValue( fet );
6540
6541 return result;
6542}
6543
6544static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6545{
6546 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6547 bool foundLayer = false;
6548 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6549
6550 //no layer found
6551 if ( !featureSource || !foundLayer )
6552 {
6553 return QVariant();
6554 }
6556 QString cacheValueKey;
6557 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6558 {
6559 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6560
6561 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6562 QString filterString;
6563 for ( ; i != attributeMap.constEnd(); ++i )
6564 {
6565 if ( !filterString.isEmpty() )
6566 {
6567 filterString.append( " AND " );
6568 }
6569 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6570 }
6571 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6572 if ( context && context->hasCachedValue( cacheValueKey ) )
6573 {
6574 return context->cachedValue( cacheValueKey );
6575 }
6576 req.setFilterExpression( filterString );
6577 }
6578 else
6579 {
6580 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6581 int attributeId = featureSource->fields().lookupField( attribute );
6582 if ( attributeId == -1 )
6583 {
6584 return QVariant();
6585 }
6586
6587 const QVariant &attVal = values.at( 2 );
6588
6589 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6590 if ( context && context->hasCachedValue( cacheValueKey ) )
6591 {
6592 return context->cachedValue( cacheValueKey );
6593 }
6594
6596 }
6597 req.setLimit( 1 );
6598 req.setTimeout( 10000 );
6599 req.setRequestMayBeNested( true );
6600 if ( context )
6601 req.setFeedback( context->feedback() );
6602 if ( !parent->needsGeometry() )
6603 {
6605 }
6606 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6607
6608 QgsFeature fet;
6609 QVariant res;
6610 if ( fIt.nextFeature( fet ) )
6611 {
6612 res = QVariant::fromValue( fet );
6613 }
6614
6615 if ( context )
6616 context->setCachedValue( cacheValueKey, res );
6617 return res;
6618}
6619
6620static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6621{
6622 QVariant result;
6623 QString fieldName;
6624
6625 if ( context )
6626 {
6627 if ( !values.isEmpty() )
6628 {
6629 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6630 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6631 fieldName = col->name();
6632 else if ( values.size() == 2 )
6633 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6634 }
6635
6636 QVariant value = values.at( 0 );
6637
6638 const QgsFields fields = context->fields();
6639 int fieldIndex = fields.lookupField( fieldName );
6640
6641 if ( fieldIndex == -1 )
6642 {
6643 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6644 }
6645 else
6646 {
6647 // TODO this function is NOT thread safe
6649 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6651
6652 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6653 if ( context->hasCachedValue( cacheValueKey ) )
6654 {
6655 return context->cachedValue( cacheValueKey );
6656 }
6657
6658 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6660
6661 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6662
6663 QVariant cache;
6664 if ( !context->hasCachedValue( cacheKey ) )
6665 {
6666 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6667 context->setCachedValue( cacheKey, cache );
6668 }
6669 else
6670 cache = context->cachedValue( cacheKey );
6671
6672 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6673
6674 context->setCachedValue( cacheValueKey, result );
6675 }
6676 }
6677 else
6678 {
6679 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6680 }
6681
6682 return result;
6683}
6684
6685static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6686{
6687 const QVariant data = values.at( 0 );
6688 const QMimeDatabase db;
6689 return db.mimeTypeForData( data.toByteArray() ).name();
6690}
6691
6692static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6693{
6694 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6695
6696 bool foundLayer = false;
6697 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6698 {
6699 if ( !layer )
6700 return QVariant();
6701
6702 // here, we always prefer the layer metadata values over the older server-specific published values
6703 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6704 return layer->name();
6705 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6706 return layer->id();
6707 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6708 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
6709 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6710 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
6711 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6712 {
6713 QStringList keywords;
6714 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6715 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6716 {
6717 keywords.append( it.value() );
6718 }
6719 if ( !keywords.isEmpty() )
6720 return keywords;
6721 return layer->serverProperties()->keywordList();
6722 }
6723 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6724 return layer->serverProperties()->dataUrl();
6725 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6726 {
6727 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
6728 }
6729 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6730 return layer->serverProperties()->attributionUrl();
6731 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6732 return layer->publicSource();
6733 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6734 return layer->minimumScale();
6735 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6736 return layer->maximumScale();
6737 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6738 return layer->isEditable();
6739 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6740 return layer->crs().authid();
6741 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6742 return layer->crs().toProj();
6743 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6744 return layer->crs().description();
6745 else if ( QString::compare( layerProperty, QStringLiteral( "crs_ellipsoid" ), Qt::CaseInsensitive ) == 0 )
6746 return layer->crs().ellipsoidAcronym();
6747 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6748 {
6749 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6750 QVariant result = QVariant::fromValue( extentGeom );
6751 return result;
6752 }
6753 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6754 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6755 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6756 {
6757 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6758 return decodedUri.value( QStringLiteral( "path" ) );
6759 }
6760 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6761 {
6762 switch ( layer->type() )
6763 {
6765 return QCoreApplication::translate( "expressions", "Vector" );
6767 return QCoreApplication::translate( "expressions", "Raster" );
6769 return QCoreApplication::translate( "expressions", "Mesh" );
6771 return QCoreApplication::translate( "expressions", "Vector Tile" );
6773 return QCoreApplication::translate( "expressions", "Plugin" );
6775 return QCoreApplication::translate( "expressions", "Annotation" );
6777 return QCoreApplication::translate( "expressions", "Point Cloud" );
6779 return QCoreApplication::translate( "expressions", "Group" );
6781 return QCoreApplication::translate( "expressions", "Tiled Scene" );
6782 }
6783 }
6784 else
6785 {
6786 //vector layer methods
6787 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6788 if ( vLayer )
6789 {
6790 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6791 return vLayer->storageType();
6792 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6794 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6795 return QVariant::fromValue( vLayer->featureCount() );
6796 }
6797 }
6798
6799 return QVariant();
6800 }, foundLayer );
6801
6802 if ( !foundLayer )
6803 return QVariant();
6804 else
6805 return res;
6806}
6807
6808static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6809{
6810 const QString uriPart = values.at( 1 ).toString();
6811
6812 bool foundLayer = false;
6813
6814 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
6815 {
6816 if ( !layer->dataProvider() )
6817 {
6818 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
6819 return QVariant();
6820 }
6821
6822 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
6823
6824 if ( !uriPart.isNull() )
6825 {
6826 return decodedUri.value( uriPart );
6827 }
6828 else
6829 {
6830 return decodedUri;
6831 }
6832 }, foundLayer );
6833
6834 if ( !foundLayer )
6835 {
6836 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
6837 return QVariant();
6838 }
6839 else
6840 {
6841 return res;
6842 }
6843}
6844
6845static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6846{
6847 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6848 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6849
6850 bool foundLayer = false;
6851 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
6852 {
6853 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
6854 if ( !rl )
6855 return QVariant();
6856
6857 if ( band < 1 || band > rl->bandCount() )
6858 {
6859 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
6860 return QVariant();
6861 }
6862
6864
6865 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
6867 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
6869 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
6871 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6873 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6875 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6877 else
6878 {
6879 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6880 return QVariant();
6881 }
6882
6883 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6884 switch ( stat )
6885 {
6887 return stats.mean;
6889 return stats.stdDev;
6891 return stats.minimumValue;
6893 return stats.maximumValue;
6895 return stats.range;
6897 return stats.sum;
6898 default:
6899 break;
6900 }
6901 return QVariant();
6902 }, foundLayer );
6903
6904 if ( !foundLayer )
6905 {
6906#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
6907 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
6908#endif
6909 return QVariant();
6910 }
6911 else
6912 {
6913 return res;
6914 }
6915}
6916
6917static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6918{
6919 return values;
6920}
6921
6922static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6923{
6924 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6925 bool ascending = values.value( 1 ).toBool();
6926 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6927 return list;
6928}
6929
6930static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6931{
6932 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6933}
6934
6935static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6936{
6937 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6938}
6939
6940static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6941{
6942 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6943}
6944
6945static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6946{
6947 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6948 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6949 int match = 0;
6950 for ( const auto &item : listB )
6951 {
6952 if ( listA.contains( item ) )
6953 match++;
6954 }
6955
6956 return QVariant( match == listB.count() );
6957}
6958
6959static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6960{
6961 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
6962}
6963
6964static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6965{
6966 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6967 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6968 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
6969 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
6970 return list.at( list.length() + pos );
6971 return QVariant();
6972}
6973
6974static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6975{
6976 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6977 return list.value( 0 );
6978}
6979
6980static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6981{
6982 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6983 return list.value( list.size() - 1 );
6984}
6985
6986static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6987{
6988 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6989 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6990}
6991
6992static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6993{
6994 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6995 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6996}
6997
6998static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6999{
7000 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7001 int i = 0;
7002 double total = 0.0;
7003 for ( const QVariant &item : list )
7004 {
7005 switch ( item.userType() )
7006 {
7007 case QMetaType::Int:
7008 case QMetaType::UInt:
7009 case QMetaType::LongLong:
7010 case QMetaType::ULongLong:
7011 case QMetaType::Float:
7012 case QMetaType::Double:
7013 total += item.toDouble();
7014 ++i;
7015 break;
7016 }
7017 }
7018 return i == 0 ? QVariant() : total / i;
7019}
7020
7021static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7022{
7023 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7024 QVariantList numbers;
7025 for ( const auto &item : list )
7026 {
7027 switch ( item.userType() )
7028 {
7029 case QMetaType::Int:
7030 case QMetaType::UInt:
7031 case QMetaType::LongLong:
7032 case QMetaType::ULongLong:
7033 case QMetaType::Float:
7034 case QMetaType::Double:
7035 numbers.append( item );
7036 break;
7037 }
7038 }
7039 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7040 const int count = numbers.count();
7041 if ( count == 0 )
7042 {
7043 return QVariant();
7044 }
7045 else if ( count % 2 )
7046 {
7047 return numbers.at( count / 2 );
7048 }
7049 else
7050 {
7051 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7052 }
7053}
7054
7055static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7056{
7057 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7058 int i = 0;
7059 double total = 0.0;
7060 for ( const QVariant &item : list )
7061 {
7062 switch ( item.userType() )
7063 {
7064 case QMetaType::Int:
7065 case QMetaType::UInt:
7066 case QMetaType::LongLong:
7067 case QMetaType::ULongLong:
7068 case QMetaType::Float:
7069 case QMetaType::Double:
7070 total += item.toDouble();
7071 ++i;
7072 break;
7073 }
7074 }
7075 return i == 0 ? QVariant() : total;
7076}
7077
7078static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7079{
7080 QVariant result = value;
7081 result.convert( static_cast<int>( type ) );
7082 return result;
7083}
7084
7085static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7086{
7087 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7088 QHash< QVariant, int > hash;
7089 for ( const auto &item : list )
7090 {
7091 ++hash[item];
7092 }
7093 const QList< int > occurrences = hash.values();
7094 if ( occurrences.empty() )
7095 return QVariantList();
7096
7097 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7098
7099 const QString option = values.at( 1 ).toString();
7100 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7101 {
7102 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7103 }
7104 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7105 {
7106 if ( hash.isEmpty() )
7107 return QVariant();
7108
7109 return QVariant( hash.key( maxValue ) );
7110 }
7111 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7112 {
7113 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7114 }
7115 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
7116 {
7117 if ( maxValue * 2 <= list.size() )
7118 return QVariant();
7119
7120 return QVariant( hash.key( maxValue ) );
7121 }
7122 else
7123 {
7124 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7125 return QVariant();
7126 }
7127}
7128
7129static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7130{
7131 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7132 QHash< QVariant, int > hash;
7133 for ( const auto &item : list )
7134 {
7135 ++hash[item];
7136 }
7137 const QList< int > occurrences = hash.values();
7138 if ( occurrences.empty() )
7139 return QVariantList();
7140
7141 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7142
7143 const QString option = values.at( 1 ).toString();
7144 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7145 {
7146 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7147 }
7148 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7149 {
7150 if ( hash.isEmpty() )
7151 return QVariant();
7152
7153 return QVariant( hash.key( minValue ) );
7154 }
7155 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7156 {
7157 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7158 }
7159 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
7160 {
7161 if ( hash.isEmpty() )
7162 return QVariant();
7163
7164 // Remove the majority, all others are minority
7165 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7166 if ( maxValue * 2 > list.size() )
7167 hash.remove( hash.key( maxValue ) );
7168
7169 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7170 }
7171 else
7172 {
7173 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7174 return QVariant();
7175 }
7176}
7177
7178static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7179{
7180 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7181 list.append( values.at( 1 ) );
7182 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7183}
7184
7185static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7186{
7187 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7188 list.prepend( values.at( 1 ) );
7189 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7190}
7191
7192static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7193{
7194 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7195 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7196 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7197}
7198
7199static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7200{
7201 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7202 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7203 if ( position < 0 )
7204 position = position + list.length();
7205 if ( position >= 0 && position < list.length() )
7206 list.removeAt( position );
7207 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7208}
7209
7210static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7211{
7212 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7213 return QVariant();
7214
7215 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7216
7217 const QVariant toRemove = values.at( 1 );
7218 if ( QgsVariantUtils::isNull( toRemove ) )
7219 {
7220 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7221 {
7222 return QgsVariantUtils::isNull( element );
7223 } ), list.end() );
7224 }
7225 else
7226 {
7227 list.removeAll( toRemove );
7228 }
7229 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7230}
7231
7232static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7233{
7234 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7235 {
7236 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7237
7238 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7239 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7240 {
7241 int index = list.indexOf( it.key() );
7242 while ( index >= 0 )
7243 {
7244 list.replace( index, it.value() );
7245 index = list.indexOf( it.key() );
7246 }
7247 }
7248
7249 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7250 }
7251 else if ( values.count() == 3 )
7252 {
7253 QVariantList before;
7254 QVariantList after;
7255 bool isSingleReplacement = false;
7256
7257 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7258 {
7259 before = QVariantList() << values.at( 1 );
7260 }
7261 else
7262 {
7263 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7264 }
7265
7266 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7267 {
7268 after = QVariantList() << values.at( 2 );
7269 isSingleReplacement = true;
7270 }
7271 else
7272 {
7273 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7274 }
7275
7276 if ( !isSingleReplacement && before.length() != after.length() )
7277 {
7278 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7279 return QVariant();
7280 }
7281
7282 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7283 for ( int i = 0; i < before.length(); i++ )
7284 {
7285 int index = list.indexOf( before.at( i ) );
7286 while ( index >= 0 )
7287 {
7288 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7289 index = list.indexOf( before.at( i ) );
7290 }
7291 }
7292
7293 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7294 }
7295 else
7296 {
7297 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7298 return QVariant();
7299 }
7300}
7301
7302static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7303{
7304 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7305 QVariantList list_new;
7306
7307 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7308 {
7309 while ( list.removeOne( cur ) )
7310 {
7311 list_new.append( cur );
7312 }
7313 }
7314
7315 list_new.append( list );
7316
7317 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7318}
7319
7320static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7321{
7322 QVariantList list;
7323 for ( const QVariant &cur : values )
7324 {
7325 list += QgsExpressionUtils::getListValue( cur, parent );
7326 }
7327 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7328}
7329
7330static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7331{
7332 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7333 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7334 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7335 int slice_length = 0;
7336 // negative positions means positions taken relative to the end of the array
7337 if ( start_pos < 0 )
7338 {
7339 start_pos = list.length() + start_pos;
7340 }
7341 if ( end_pos >= 0 )
7342 {
7343 slice_length = end_pos - start_pos + 1;
7344 }
7345 else
7346 {
7347 slice_length = list.length() + end_pos - start_pos + 1;
7348 }
7349 //avoid negative lengths in QList.mid function
7350 if ( slice_length < 0 )
7351 {
7352 slice_length = 0;
7353 }
7354 list = list.mid( start_pos, slice_length );
7355 return list;
7356}
7357
7358static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7359{
7360 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7361 std::reverse( list.begin(), list.end() );
7362 return list;
7363}
7364
7365static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7366{
7367 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7368 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7369 for ( const QVariant &cur : array2 )
7370 {
7371 if ( array1.contains( cur ) )
7372 return QVariant( true );
7373 }
7374 return QVariant( false );
7375}
7376
7377static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7378{
7379 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7380
7381 QVariantList distinct;
7382
7383 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7384 {
7385 if ( !distinct.contains( *it ) )
7386 {
7387 distinct += ( *it );
7388 }
7389 }
7390
7391 return distinct;
7392}
7393
7394static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7395{
7396 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7397 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7398 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7399
7400 QString str;
7401
7402 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7403 {
7404 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7405 if ( it != ( array.constEnd() - 1 ) )
7406 {
7407 str += delimiter;
7408 }
7409 }
7410
7411 return QVariant( str );
7412}
7413
7414static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7415{
7416 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7417 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7418 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7419
7420 QStringList list = str.split( delimiter );
7421 QVariantList array;
7422
7423 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7424 {
7425 array += ( !( *it ).isEmpty() ) ? *it : empty;
7426 }
7427
7428 return array;
7429}
7430
7431static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7432{
7433 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7434 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7435 if ( document.isNull() )
7436 return QVariant();
7437
7438 return document.toVariant();
7439}
7440
7441static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7442{
7443 Q_UNUSED( parent )
7444 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7445 return QString( document.toJson( QJsonDocument::Compact ) );
7446}
7447
7448static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7449{
7450 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7451 if ( str.isEmpty() )
7452 return QVariantMap();
7453 str = str.trimmed();
7454
7455 return QgsHstoreUtils::parse( str );
7456}
7457
7458static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7459{
7460 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7461 return QgsHstoreUtils::build( map );
7462}
7463
7464static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7465{
7466 QVariantMap result;
7467 for ( int i = 0; i + 1 < values.length(); i += 2 )
7468 {
7469 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7470 }
7471 return result;
7472}
7473
7474static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7475{
7476 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7477 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7478 QVariantMap resultMap;
7479
7480 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7481 {
7482 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7483 }
7484
7485 return resultMap;
7486}
7487
7488static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7489{
7490 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7491}
7492
7493static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7494{
7495 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7496}
7497
7498static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7499{
7500 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7501 map.remove( values.at( 1 ).toString() );
7502 return map;
7503}
7504
7505static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7506{
7507 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7508 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7509 return map;
7510}
7511
7512static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7513{
7514 QVariantMap result;
7515 for ( const QVariant &cur : values )
7516 {
7517 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7518 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7519 result.insert( it.key(), it.value() );
7520 }
7521 return result;
7522}
7523
7524static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7525{
7526 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7527}
7528
7529static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7530{
7531 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7532}
7533
7534static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7535{
7536 const QString envVarName = values.at( 0 ).toString();
7537 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7538 return QVariant();
7539
7540 return QProcessEnvironment::systemEnvironment().value( envVarName );
7541}
7542
7543static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7544{
7545 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7546 if ( parent->hasEvalError() )
7547 {
7548 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7549 return QVariant();
7550 }
7551 return QFileInfo( file ).completeBaseName();
7552}
7553
7554static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7555{
7556 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7557 if ( parent->hasEvalError() )
7558 {
7559 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7560 return QVariant();
7561 }
7562 return QFileInfo( file ).completeSuffix();
7563}
7564
7565static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7566{
7567 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7568 if ( parent->hasEvalError() )
7569 {
7570 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7571 return QVariant();
7572 }
7573 return QFileInfo::exists( file );
7574}
7575
7576static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7577{
7578 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7579 if ( parent->hasEvalError() )
7580 {
7581 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7582 return QVariant();
7583 }
7584 return QFileInfo( file ).fileName();
7585}
7586
7587static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7588{
7589 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7590 if ( parent->hasEvalError() )
7591 {
7592 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7593 return QVariant();
7594 }
7595 return QFileInfo( file ).isFile();
7596}
7597
7598static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7599{
7600 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7601 if ( parent->hasEvalError() )
7602 {
7603 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7604 return QVariant();
7605 }
7606 return QFileInfo( file ).isDir();
7607}
7608
7609static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7610{
7611 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7612 if ( parent->hasEvalError() )
7613 {
7614 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7615 return QVariant();
7616 }
7617 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7618}
7619
7620static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7621{
7622 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7623 if ( parent->hasEvalError() )
7624 {
7625 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7626 return QVariant();
7627 }
7628 return QFileInfo( file ).size();
7629}
7630
7631static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7632{
7633 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7634}
7635
7636static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7637{
7638 QVariant hash;
7639 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7640 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7641
7642 if ( method == QLatin1String( "md4" ) )
7643 {
7644 hash = fcnHash( str, QCryptographicHash::Md4 );
7645 }
7646 else if ( method == QLatin1String( "md5" ) )
7647 {
7648 hash = fcnHash( str, QCryptographicHash::Md5 );
7649 }
7650 else if ( method == QLatin1String( "sha1" ) )
7651 {
7652 hash = fcnHash( str, QCryptographicHash::Sha1 );
7653 }
7654 else if ( method == QLatin1String( "sha224" ) )
7655 {
7656 hash = fcnHash( str, QCryptographicHash::Sha224 );
7657 }
7658 else if ( method == QLatin1String( "sha256" ) )
7659 {
7660 hash = fcnHash( str, QCryptographicHash::Sha256 );
7661 }
7662 else if ( method == QLatin1String( "sha384" ) )
7663 {
7664 hash = fcnHash( str, QCryptographicHash::Sha384 );
7665 }
7666 else if ( method == QLatin1String( "sha512" ) )
7667 {
7668 hash = fcnHash( str, QCryptographicHash::Sha512 );
7669 }
7670 else if ( method == QLatin1String( "sha3_224" ) )
7671 {
7672 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7673 }
7674 else if ( method == QLatin1String( "sha3_256" ) )
7675 {
7676 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7677 }
7678 else if ( method == QLatin1String( "sha3_384" ) )
7679 {
7680 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7681 }
7682 else if ( method == QLatin1String( "sha3_512" ) )
7683 {
7684 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7685 }
7686 else if ( method == QLatin1String( "keccak_224" ) )
7687 {
7688 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7689 }
7690 else if ( method == QLatin1String( "keccak_256" ) )
7691 {
7692 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7693 }
7694 else if ( method == QLatin1String( "keccak_384" ) )
7695 {
7696 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7697 }
7698 else if ( method == QLatin1String( "keccak_512" ) )
7699 {
7700 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7701 }
7702 else
7703 {
7704 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7705 }
7706 return hash;
7707}
7708
7709static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7710{
7711 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7712}
7713
7714static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7715{
7716 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7717}
7718
7719static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7720{
7721 const QByteArray input = values.at( 0 ).toByteArray();
7722 return QVariant( QString( input.toBase64() ) );
7723}
7724
7725static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7726{
7727 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7728 QUrlQuery query;
7729 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7730 {
7731 query.addQueryItem( it.key(), it.value().toString() );
7732 }
7733 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7734}
7735
7736static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7737{
7738 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7739 const QByteArray base64 = value.toLocal8Bit();
7740 const QByteArray decoded = QByteArray::fromBase64( base64 );
7741 return QVariant( decoded );
7742}
7743
7744typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7745
7746static 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 )
7747{
7748
7749 if ( ! context )
7750 {
7751 parent->setEvalErrorString( QStringLiteral( "This function was called without an expression context." ) );
7752 return QVariant();
7753 }
7754
7755 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7756 // TODO this function is NOT thread safe
7758 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7760
7761 QgsFeatureRequest request;
7762 request.setTimeout( 10000 );
7763 request.setRequestMayBeNested( true );
7764 request.setFeedback( context->feedback() );
7765
7766 // First parameter is the overlay layer
7767 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7769
7770 const bool layerCanBeCached = node->isStatic( parent, context );
7771 QVariant targetLayerValue = node->eval( parent, context );
7773
7774 // Second parameter is the expression to evaluate (or null for testonly)
7775 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7777 QString subExpString = node->dump();
7778
7779 bool testOnly = ( subExpString == "NULL" );
7780 // TODO this function is NOT thread safe
7782 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7784 if ( !targetLayer ) // No layer, no joy
7785 {
7786 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7787 return QVariant();
7788 }
7789
7790 // Third parameter is the filtering expression
7791 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7793 QString filterString = node->dump();
7794 if ( filterString != "NULL" )
7795 {
7796 request.setFilterExpression( filterString ); //filter cached features
7797 }
7798
7799 // Fourth parameter is the limit
7800 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7802 QVariant limitValue = node->eval( parent, context );
7804 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7805
7806 // Fifth parameter (for nearest only) is the max distance
7807 double max_distance = 0;
7808 if ( isNearestFunc ) //maxdistance param handling
7809 {
7810 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7812 QVariant distanceValue = node->eval( parent, context );
7814 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
7815 }
7816
7817 // Fifth or sixth (for nearest only) parameter is the cache toggle
7818 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
7820 QVariant cacheValue = node->eval( parent, context );
7822 bool cacheEnabled = cacheValue.toBool();
7823
7824 // Sixth parameter (for intersects only) is the min overlap (area or length)
7825 // Seventh parameter (for intersects only) is the min inscribed circle radius
7826 // Eighth parameter (for intersects only) is the return_details
7827 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
7828 double minOverlap { -1 };
7829 double minInscribedCircleRadius { -1 };
7830 bool returnDetails = false; //#spellok
7831 bool sortByMeasure = false;
7832 bool sortAscending = false;
7833 bool requireMeasures = false;
7834 bool overlapOrRadiusFilter = false;
7835 if ( isIntersectsFunc )
7836 {
7837
7838 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7840 const QVariant minOverlapValue = node->eval( parent, context );
7842 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
7843 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7845 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
7847 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
7848 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
7849 // Return measures is only effective when an expression is set
7850 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
7851 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
7852 // Sort by measures is only effective when an expression is set
7853 const QString sorting { node->eval( parent, context ).toString().toLower() };
7854 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
7855 sortAscending = sorting.startsWith( "asc" );
7856 requireMeasures = sortByMeasure || returnDetails; //#spellok
7857 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
7858 }
7859
7860
7861 FEAT_FROM_CONTEXT( context, feat )
7862 const QgsGeometry geometry = feat.geometry();
7863
7864 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
7865 {
7866 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
7867 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
7868 }
7869
7870 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
7871
7872 QgsRectangle intDomain = geometry.boundingBox();
7873 if ( bboxGrow != 0 )
7874 {
7875 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
7876 }
7877
7878 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
7879
7880 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
7881 // Otherwise, it can be toggled by the user
7882 QgsSpatialIndex spatialIndex;
7883 QgsVectorLayer *cachedTarget;
7884 QList<QgsFeature> features;
7885 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
7886 {
7887 // If the cache (local spatial index) is enabled, we materialize the whole
7888 // layer, then do the request on that layer instead.
7889 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
7890 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
7891
7892 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
7893 {
7894 cachedTarget = targetLayer->materialize( request );
7895 if ( layerCanBeCached )
7896 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
7897 }
7898 else
7899 {
7900 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
7901 }
7902
7903 if ( !context->hasCachedValue( cacheIndex ) )
7904 {
7905 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
7906 if ( layerCanBeCached )
7907 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
7908 }
7909 else
7910 {
7911 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
7912 }
7913
7914 QList<QgsFeatureId> fidsList;
7915 if ( isNearestFunc )
7916 {
7917 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7918 }
7919 else
7920 {
7921 fidsList = spatialIndex.intersects( intDomain );
7922 }
7923
7924 QListIterator<QgsFeatureId> i( fidsList );
7925 while ( i.hasNext() )
7926 {
7927 QgsFeatureId fId2 = i.next();
7928 if ( sameLayers && feat.id() == fId2 )
7929 continue;
7930 features.append( cachedTarget->getFeature( fId2 ) );
7931 }
7932
7933 }
7934 else
7935 {
7936 // If the cache (local spatial index) is not enabled, we directly
7937 // get the features from the target layer
7938 request.setFilterRect( intDomain );
7939 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7940 QgsFeature feat2;
7941 while ( fit.nextFeature( feat2 ) )
7942 {
7943 if ( sameLayers && feat.id() == feat2.id() )
7944 continue;
7945 features.append( feat2 );
7946 }
7947 }
7948
7949 QgsExpression subExpression;
7950 QgsExpressionContext subContext;
7951 if ( !testOnly )
7952 {
7953 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7954 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7955
7956 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7957 {
7958 subExpression = QgsExpression( subExpString );
7960 subExpression.prepare( &subContext );
7961 }
7962 else
7963 {
7964 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
7965 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
7966 }
7967 }
7968
7969 // //////////////////////////////////////////////////////////////////
7970 // Helper functions for geometry tests
7971
7972 // Test function for linestring geometries, returns TRUE if test passes
7973 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
7974 {
7975 bool testResult { false };
7976 // For return measures:
7977 QVector<double> overlapValues;
7978 const QgsGeometry merged { intersection.mergeLines() };
7979 for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
7980 {
7981 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
7982 // Check min overlap for intersection (if set)
7983 if ( minOverlap != -1 || requireMeasures )
7984 {
7985 overlapValue = geom->length();
7986 overlapValues.append( overlapValue );
7987 if ( minOverlap != -1 )
7988 {
7989 if ( overlapValue >= minOverlap )
7990 {
7991 testResult = true;
7992 }
7993 else
7994 {
7995 continue;
7996 }
7997 }
7998 }
7999 }
8000
8001 if ( ! overlapValues.isEmpty() )
8002 {
8003 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8004 }
8005
8006 return testResult;
8007 };
8008
8009 // Test function for polygon geometries, returns TRUE if test passes
8010 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
8011 {
8012 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8013 bool testResult { false };
8014 // For return measures:
8015 QVector<double> overlapValues;
8016 QVector<double> radiusValues;
8017 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8018 {
8019 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
8020 // Check min overlap for intersection (if set)
8021 if ( minOverlap != -1 || requireMeasures )
8022 {
8023 overlapValue = geom->area();
8024 overlapValues.append( geom->area() );
8025 if ( minOverlap != - 1 )
8026 {
8027 if ( overlapValue >= minOverlap )
8028 {
8029 testResult = true;
8030 }
8031 else
8032 {
8033 continue;
8034 }
8035 }
8036 }
8037
8038 // Check min inscribed circle radius for intersection (if set)
8039 if ( minInscribedCircleRadius != -1 || requireMeasures )
8040 {
8041 const QgsRectangle bbox = geom->boundingBox();
8042 const double width = bbox.width();
8043 const double height = bbox.height();
8044 const double size = width > height ? width : height;
8045 const double tolerance = size / 100.0;
8046 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8047 testResult = radiusValue >= minInscribedCircleRadius;
8048 radiusValues.append( radiusValues );
8049 }
8050 } // end for parts
8051
8052 // Get the max values
8053 if ( !radiusValues.isEmpty() )
8054 {
8055 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8056 }
8057
8058 if ( ! overlapValues.isEmpty() )
8059 {
8060 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8061 }
8062
8063 return testResult;
8064
8065 };
8066
8067
8068 bool found = false;
8069 int foundCount = 0;
8070 QVariantList results;
8071
8072 QListIterator<QgsFeature> i( features );
8073 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8074 {
8075
8076 QgsFeature feat2 = i.next();
8077
8078
8079 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8080 {
8081
8082 double overlapValue = -1;
8083 double radiusValue = -1;
8084
8085 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8086 {
8087
8088 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8089
8090 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8091 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8092 {
8093 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8094 intersection = QgsGeometry();
8095 QgsMultiPolygonXY poly;
8096 QgsMultiPolylineXY line;
8097 QgsMultiPointXY point;
8098 for ( const auto &geom : std::as_const( geometries ) )
8099 {
8100 switch ( geom.type() )
8101 {
8103 {
8104 poly.append( geom.asPolygon() );
8105 break;
8106 }
8108 {
8109 line.append( geom.asPolyline() );
8110 break;
8111 }
8113 {
8114 point.append( geom.asPoint() );
8115 break;
8116 }
8119 {
8120 break;
8121 }
8122 }
8123 }
8124
8125 switch ( geometry.type() )
8126 {
8128 {
8129 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8130 break;
8131 }
8133 {
8134 intersection = QgsGeometry::fromMultiPolylineXY( line );
8135 break;
8136 }
8138 {
8139 intersection = QgsGeometry::fromMultiPointXY( point );
8140 break;
8141 }
8144 {
8145 break;
8146 }
8147 }
8148 }
8149
8150 // Depending on the intersection geometry type and on the geometry type of
8151 // the tested geometry we can run different tests and collect different measures
8152 // that can be used for sorting (if required).
8153 switch ( intersection.type() )
8154 {
8155
8157 {
8158
8159 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8160 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8161
8162 if ( ! testResult && overlapOrRadiusFilter )
8163 {
8164 continue;
8165 }
8166
8167 break;
8168 }
8169
8171 {
8172
8173 // If the intersection is a linestring and a minimum circle is required
8174 // we can discard this result immediately.
8175 if ( minInscribedCircleRadius != -1 )
8176 {
8177 continue;
8178 }
8179
8180 // Otherwise a test for the overlap value is performed.
8181 const bool testResult { testLinestring( intersection, overlapValue ) };
8182
8183 if ( ! testResult && overlapOrRadiusFilter )
8184 {
8185 continue;
8186 }
8187
8188 break;
8189 }
8190
8192 {
8193
8194 // If the intersection is a point and a minimum circle is required
8195 // we can discard this result immediately.
8196 if ( minInscribedCircleRadius != -1 )
8197 {
8198 continue;
8199 }
8200
8201 bool testResult { false };
8202 if ( minOverlap != -1 || requireMeasures )
8203 {
8204 // Initially set this to 0 because it's a point intersection...
8205 overlapValue = 0;
8206 // ... but if the target geometry is not a point and the source
8207 // geometry is a point, we must record the length or the area
8208 // of the intersected geometry and use that as a measure for
8209 // sorting or reporting.
8210 if ( geometry.type() == Qgis::GeometryType::Point )
8211 {
8212 switch ( feat2.geometry().type() )
8213 {
8217 {
8218 break;
8219 }
8221 {
8222 testResult = testLinestring( feat2.geometry(), overlapValue );
8223 break;
8224 }
8226 {
8227 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8228 break;
8229 }
8230 }
8231 }
8232
8233 if ( ! testResult && overlapOrRadiusFilter )
8234 {
8235 continue;
8236 }
8237
8238 }
8239 break;
8240 }
8241
8244 {
8245 continue;
8246 }
8247 }
8248 }
8249
8250 found = true;
8251 foundCount++;
8252
8253 // We just want a single boolean result if there is any intersect: finish and return true
8254 if ( testOnly )
8255 break;
8256
8257 if ( !invert )
8258 {
8259 // We want a list of attributes / geometries / other expression values, evaluate now
8260 subContext.setFeature( feat2 );
8261 const QVariant expResult = subExpression.evaluate( &subContext );
8262
8263 if ( requireMeasures )
8264 {
8265 QVariantMap resultRecord;
8266 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
8267 resultRecord.insert( QStringLiteral( "result" ), expResult );
8268 // Overlap is always added because return measures was set
8269 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
8270 // Radius is only added when is different than -1 (because for linestrings is not set)
8271 if ( radiusValue != -1 )
8272 {
8273 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
8274 }
8275 results.append( resultRecord );
8276 }
8277 else
8278 {
8279 results.append( expResult );
8280 }
8281 }
8282 else
8283 {
8284 // If not, results is a list of found ids, which we'll inverse and evaluate below
8285 results.append( feat2.id() );
8286 }
8287 }
8288 }
8289
8290 if ( testOnly )
8291 {
8292 if ( invert )
8293 found = !found;//for disjoint condition
8294 return found;
8295 }
8296
8297 if ( !invert )
8298 {
8299 if ( requireMeasures )
8300 {
8301 if ( sortByMeasure )
8302 {
8303 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8304 {
8305 return sortAscending ?
8306 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
8307 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
8308 } );
8309 }
8310 // Resize
8311 if ( limit > 0 && results.size() > limit )
8312 {
8313 results.erase( results.begin() + limit );
8314 }
8315
8316 if ( ! returnDetails ) //#spellok
8317 {
8318 QVariantList expResults;
8319 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8320 {
8321 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
8322 }
8323 return expResults;
8324 }
8325 }
8326
8327 return results;
8328 }
8329
8330 // for disjoint condition returns the results for cached layers not intersected feats
8331 QVariantList disjoint_results;
8332 QgsFeature feat2;
8333 QgsFeatureRequest request2;
8334 request2.setLimit( limit );
8335 if ( context )
8336 request2.setFeedback( context->feedback() );
8337 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8338 while ( fi.nextFeature( feat2 ) )
8339 {
8340 if ( !results.contains( feat2.id() ) )
8341 {
8342 subContext.setFeature( feat2 );
8343 disjoint_results.append( subExpression.evaluate( &subContext ) );
8344 }
8345 }
8346 return disjoint_results;
8347
8348}
8349
8350// Intersect functions:
8351
8352static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8353{
8354 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8355}
8356
8357static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8358{
8359 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8360}
8361
8362static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8363{
8364 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8365}
8366
8367static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8368{
8369 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8370}
8371
8372static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8373{
8374 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8375}
8376
8377static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8378{
8379 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8380}
8382static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8383{
8384 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8385}
8386
8387static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8388{
8389 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8390}
8391
8392const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8393{
8394 // The construction of the list isn't thread-safe, and without the mutex,
8395 // crashes in the WFS provider may occur, since it can parse expressions
8396 // in parallel.
8397 // The mutex needs to be recursive.
8398 QMutexLocker locker( &sFunctionsMutex );
8399
8400 QList<QgsExpressionFunction *> &functions = *sFunctions();
8401
8402 if ( functions.isEmpty() )
8403 {
8405 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8406 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
8407 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
8408
8409 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8410 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8411 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8412
8413 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8414 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8415
8416 functions
8417 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
8418 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
8419 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
8420 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
8421 << 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" ) )
8422 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
8423 << 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" ) )
8424 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
8425 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
8426 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
8427 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
8428 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
8429 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
8430 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
8431 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
8432 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
8433 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
8434 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
8435 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
8436 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
8437
8438 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
8439 randFunc->setIsStatic( false );
8440 functions << randFunc;
8441
8442 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" ) );
8443 randfFunc->setIsStatic( false );
8444 functions << randfFunc;
8445
8446 functions
8447 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8448 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8449 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
8450 << 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" ) )
8451 << 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" ) )
8452 << 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" ) )
8453 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8454 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8455 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8456 << new QgsStaticExpressionFunction( QStringLiteral( "to_bool" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToBool, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tobool" ), /* handlesNull = */ true )
8457 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8458 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8459 << 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" ) )
8460 << 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" ) )
8461 << 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" ) )
8462 << 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" ) )
8463 << 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" ) )
8464 << 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" ) )
8465 << 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" ) )
8466 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8467 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8468 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8469 << 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 )
8470 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8471
8472 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8474 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8475 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8476 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8477 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8478 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8479 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8480 fcnAggregate,
8481 QStringLiteral( "Aggregates" ),
8482 QString(),
8483 []( const QgsExpressionNodeFunction * node )
8484 {
8485 // usesGeometry callback: return true if @parent variable is referenced
8486
8487 if ( !node )
8488 return true;
8489
8490 if ( !node->args() )
8491 return false;
8492
8493 QSet<QString> referencedVars;
8494 if ( node->args()->count() > 2 )
8495 {
8496 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8497 referencedVars = subExpressionNode->referencedVariables();
8498 }
8499
8500 if ( node->args()->count() > 3 )
8501 {
8502 QgsExpressionNode *filterNode = node->args()->at( 3 );
8503 referencedVars.unite( filterNode->referencedVariables() );
8504 }
8505 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8506 },
8507 []( const QgsExpressionNodeFunction * node )
8508 {
8509 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8510
8511 if ( !node )
8512 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8513
8514 if ( !node->args() )
8515 return QSet<QString>();
8516
8517 QSet<QString> referencedCols;
8518 QSet<QString> referencedVars;
8519
8520 if ( node->args()->count() > 2 )
8521 {
8522 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8523 referencedVars = subExpressionNode->referencedVariables();
8524 referencedCols = subExpressionNode->referencedColumns();
8525 }
8526 if ( node->args()->count() > 3 )
8527 {
8528 QgsExpressionNode *filterNode = node->args()->at( 3 );
8529 referencedVars = filterNode->referencedVariables();
8530 referencedCols.unite( filterNode->referencedColumns() );
8531 }
8532
8533 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8534 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8535 else
8536 return referencedCols;
8537 },
8538 true
8539 )
8540
8541 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8542 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8543 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8544 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8545 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8546 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8547 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8548
8549 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8550 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8551 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8552 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8553 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8554 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8555 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8556 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8557 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8558 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8559 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8560 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8561 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8562 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8563 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8564 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8565 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8566 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8567 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8568 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8569 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8570
8571 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8572 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8573
8574 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8575 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8576 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8577 fcnAge, QStringLiteral( "Date and Time" ) )
8578 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8579 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8580 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8581 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8582 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8583 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8584 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8585 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8586 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8587 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8588 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8589 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8590 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8591 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8592 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8593 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8594 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8595 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8596 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8597 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8598 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8599 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8600 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8601 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8602 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8603 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8604 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8605 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8606 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8607 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8608 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8609 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8610 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8611 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8612 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8613 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8614 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8615 << new QgsStaticExpressionFunction( QStringLiteral( "ltrim" ), QgsExpressionFunction::ParameterList()
8616 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8617 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnLTrim, QStringLiteral( "String" ) )
8618 << new QgsStaticExpressionFunction( QStringLiteral( "rtrim" ), QgsExpressionFunction::ParameterList()
8619 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8620 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnRTrim, QStringLiteral( "String" ) )
8621 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8622 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8623 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8624 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8625 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8626 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8627 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8628 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8629 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8630 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8631 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8632 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8633 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8634 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8635 false, QSet< QString >(), false, QStringList(), true )
8636 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8637 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8638 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8639 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8640 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8641 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8642 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8643 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8644 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8645 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8646 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8647 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8648 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8649 << 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" ) )
8650 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8651 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8652 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8653 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8654 fcnColorMixRgb, QStringLiteral( "Color" ) )
8655 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8656 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8657 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8658 fcnColorMix, QStringLiteral( "Color" ) )
8659 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8660 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8661 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8662 fcnColorRgb, QStringLiteral( "Color" ) )
8663 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgbf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8664 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8665 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8666 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8667 fcnColorRgbF, QStringLiteral( "Color" ) )
8668 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8669 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8670 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8671 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8672 fncColorRgba, QStringLiteral( "Color" ) )
8673 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8674 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8675 fcnRampColor, QStringLiteral( "Color" ) )
8676 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color_object" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8677 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8678 fcnRampColorObject, QStringLiteral( "Color" ) )
8679 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8680 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8681 fcnCreateRamp, QStringLiteral( "Color" ) )
8682 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8683 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8684 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8685 fcnColorHsl, QStringLiteral( "Color" ) )
8686 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8687 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8688 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8689 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8690 fncColorHsla, QStringLiteral( "Color" ) )
8691 << new QgsStaticExpressionFunction( QStringLiteral( "color_hslf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8692 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8693 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8694 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8695 fcnColorHslF, QStringLiteral( "Color" ) )
8696 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8697 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8698 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8699 fcnColorHsv, QStringLiteral( "Color" ) )
8700 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8701 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8702 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8703 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8704 fncColorHsva, QStringLiteral( "Color" ) )
8705 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsvf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8706 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8707 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8708 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8709 fcnColorHsvF, QStringLiteral( "Color" ) )
8710 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8711 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8712 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8713 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8714 fcnColorCmyk, QStringLiteral( "Color" ) )
8715 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8716 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8717 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8718 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8719 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8720 fncColorCmyka, QStringLiteral( "Color" ) )
8721 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmykf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8722 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8723 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8724 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8725 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8726 fcnColorCmykF, QStringLiteral( "Color" ) )
8727 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8728 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8729 fncColorPart, QStringLiteral( "Color" ) )
8730 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8731 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8732 fncDarker, QStringLiteral( "Color" ) )
8733 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8734 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8735 fncLighter, QStringLiteral( "Color" ) )
8736 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8737
8738 // file info
8739 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8740 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8741 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8742 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8743 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8744 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8745 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8746 fcnFileName, QStringLiteral( "Files and Paths" ) )
8747 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8748 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8749 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8750 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8751 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8752 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8753 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8754 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8755
8756 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8757 fcnExif, QStringLiteral( "Files and Paths" ) )
8758 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8759 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8760
8761 // hash
8762 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8763 fcnGenericHash, QStringLiteral( "Conversions" ) )
8764 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8765 fcnHashMd5, QStringLiteral( "Conversions" ) )
8766 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8767 fcnHashSha256, QStringLiteral( "Conversions" ) )
8768
8769 //base64
8770 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8771 fcnToBase64, QStringLiteral( "Conversions" ) )
8772 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8773 fcnFromBase64, QStringLiteral( "Conversions" ) )
8774
8775 // deprecated stuff - hidden from users
8776 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8777
8778 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8779 geomFunc->setIsStatic( false );
8780 functions << geomFunc;
8781
8782 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8783 areaFunc->setIsStatic( false );
8784 functions << areaFunc;
8785
8786 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8787
8788 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8789 lengthFunc->setIsStatic( false );
8790 functions << lengthFunc;
8791
8792 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8793 perimeterFunc->setIsStatic( false );
8794 functions << perimeterFunc;
8795
8796 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8797
8798 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8800 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
8801
8802 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
8803 xFunc->setIsStatic( false );
8804 functions << xFunc;
8805
8806 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
8807 yFunc->setIsStatic( false );
8808 functions << yFunc;
8809
8810 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
8811 zFunc->setIsStatic( false );
8812 functions << zFunc;
8813
8814 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
8815 {
8816 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
8817 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
8818 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
8819 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
8820 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
8821 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
8822 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
8823 };
8824 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
8825 while ( i.hasNext() )
8826 {
8827 i.next();
8829 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8830 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8831 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8832 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
8833 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
8834 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
8835 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
8836 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
8837 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
8838 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8839
8840 // The current feature is accessed for the geometry, so this should not be cached
8841 fcnGeomOverlayFunc->setIsStatic( false );
8842 functions << fcnGeomOverlayFunc;
8843 }
8844
8845 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
8846 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8847 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
8848 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8849 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
8850 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
8851 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
8852 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
8853 // The current feature is accessed for the geometry, so this should not be cached
8854 fcnGeomOverlayNearestFunc->setIsStatic( false );
8855 functions << fcnGeomOverlayNearestFunc;
8856
8857 functions
8858 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
8859 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
8860 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
8861 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
8862 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
8863 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
8864 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
8865 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
8866 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8867 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
8868 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
8869 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
8870 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
8871 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
8872 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8873 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
8874 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
8875 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
8876 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
8877 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
8878 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8879 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8880 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
8881 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
8882 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
8883 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8884 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8885 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8886 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
8887 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
8888 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8889 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
8890 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
8891 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
8892 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8893 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
8894 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
8895 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
8896 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
8897 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
8898 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
8899 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
8900 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
8901 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8902 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
8903 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
8904 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
8905 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
8906 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
8907 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
8908 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
8909 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
8910 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
8911 {
8912 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8913#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
8914 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
8915#else
8916 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
8917#endif
8918 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
8919 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
8920
8921 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
8922 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
8923 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
8924 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
8925
8926 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
8927 xAtFunc->setIsStatic( false );
8928 functions << xAtFunc;
8929
8930
8931 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
8932 yAtFunc->setIsStatic( false );
8933 functions << yAtFunc;
8934
8935 functions
8936 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
8937 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
8938 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
8939 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
8940 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
8941 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
8942 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8943 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
8944 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
8945 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
8946 << 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" ) )
8947 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8948 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8949 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
8950 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8951 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8952 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
8953 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8954 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8955 fcnTouches, QStringLiteral( "GeometryGroup" ) )
8956 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8957 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8958 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
8959 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8960 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8961 fcnContains, QStringLiteral( "GeometryGroup" ) )
8962 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8963 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8964 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
8965 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8966 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8967 fcnWithin, QStringLiteral( "GeometryGroup" ) )
8968 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8969 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
8970 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
8971 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
8972 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8973 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
8974 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
8975 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
8976 fcnRotate, QStringLiteral( "GeometryGroup" ) )
8977 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8978 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
8979 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
8980 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
8981 fcnScale, QStringLiteral( "GeometryGroup" ) )
8982 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8983 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
8984 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
8985 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
8986 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
8987 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
8988 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
8989 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
8990 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
8991 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
8992 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
8993 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8994 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8995 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
8996 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
8997 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
8998 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
8999 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
9000 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9001 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
9002 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9003 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
9004 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9005 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
9006 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9007 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
9008 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
9009 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
9010 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
9011 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9012 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
9013 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
9014 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9015 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
9016 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9017 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9018 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
9019 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9020 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9021 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9022 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9023 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9024 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
9025 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9026 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9027 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9028 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9029 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9030 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
9031 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9032 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
9033 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
9034 fcnExtend, QStringLiteral( "GeometryGroup" ) )
9035 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
9036 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
9037 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9038 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
9039 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
9040 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
9041 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9042 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9043 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
9044 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9045 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9046 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
9047 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
9048 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
9049 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
9050 {
9051 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
9052 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9053 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
9054 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
9055 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
9056 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
9057 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
9058 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
9059 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
9060 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
9061 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
9062 {
9063 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9064 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9065 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9066 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9067 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
9068 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
9069 {
9070 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9071 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9072 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9073 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9074 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9075 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9076 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9077 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
9078 {
9079 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9080 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9081 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9082 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9083 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
9084 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
9085 {
9086 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9087 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9088 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9089 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9090 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9091 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9092 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9093 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
9094 {
9095 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9096 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9097 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9098 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9099 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
9100 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
9101 {
9102 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9103 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9104 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9105 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9106 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9107 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9108 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9109 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
9110 {
9111 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9112 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
9113 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
9114 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
9115 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
9116 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
9117 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
9118 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
9119 {
9120 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9121 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
9122 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
9123 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
9124 {
9125 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9126 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9127 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
9128 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
9129 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
9130 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
9131 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
9132 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
9133 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
9134 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
9135 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
9136 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
9137 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9138 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
9139#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9140 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9141 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
9142 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
9143#endif
9144 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
9145 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9146 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
9147 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
9148 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9149 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
9150 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
9151 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9152 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9153 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
9154 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9155 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9156 fcnDifference, QStringLiteral( "GeometryGroup" ) )
9157 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9158 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9159 fcnDistance, QStringLiteral( "GeometryGroup" ) )
9160 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9161 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
9162 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
9163 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9164 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9165 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
9166 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9167 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9168 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
9169 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9170 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9171 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9172 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9173 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9174 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9175 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9176 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
9177 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
9178 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9179 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9180 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
9181 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9182 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
9183 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
9184 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
9185 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9186 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9187 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
9188 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
9189 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9190 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
9191 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9192 fcnZMax, QStringLiteral( "GeometryGroup" ) )
9193 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9194 fcnZMin, QStringLiteral( "GeometryGroup" ) )
9195 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9196 fcnMMax, QStringLiteral( "GeometryGroup" ) )
9197 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9198 fcnMMin, QStringLiteral( "GeometryGroup" ) )
9199 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9200 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
9201 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9202 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
9203
9204
9205 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9206 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
9207 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
9208 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
9209
9210 orderPartsFunc->setIsStaticFunction(
9211 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9212 {
9213 const QList< QgsExpressionNode *> argList = node->args()->list();
9214 for ( QgsExpressionNode *argNode : argList )
9215 {
9216 if ( !argNode->isStatic( parent, context ) )
9217 return false;
9218 }
9219
9220 if ( node->args()->count() > 1 )
9221 {
9222 QgsExpressionNode *argNode = node->args()->at( 1 );
9223
9224 QString expString = argNode->eval( parent, context ).toString();
9225
9226 QgsExpression e( expString );
9227
9228 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9229 return true;
9230 }
9231
9232 return true;
9233 } );
9234
9235 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9236 {
9237 if ( node->args()->count() > 1 )
9238 {
9239 QgsExpressionNode *argNode = node->args()->at( 1 );
9240 QString expression = argNode->eval( parent, context ).toString();
9242 e.prepare( context );
9243 context->setCachedValue( expression, QVariant::fromValue( e ) );
9244 }
9245 return true;
9246 }
9247 );
9248 functions << orderPartsFunc;
9249
9250 functions
9251 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9252 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9253 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
9254 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9255 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9256 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
9257 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9258 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
9259 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9260 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9261 fcnLineInterpolatePointByM, QStringLiteral( "GeometryGroup" ) )
9262 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9263 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
9264 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9265 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
9266 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9267 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9268 fcnLineLocateM, QStringLiteral( "GeometryGroup" ) )
9269 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9270 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
9271 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9272 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
9273 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9274 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
9275
9276
9277 // **Record** functions
9278
9279 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
9280 idFunc->setIsStatic( false );
9281 functions << idFunc;
9282
9283 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
9284 currentFeatureFunc->setIsStatic( false );
9285 functions << currentFeatureFunc;
9286
9287 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" ) );
9288 uuidFunc->setIsStatic( false );
9289 functions << uuidFunc;
9290
9291 functions
9292 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
9293 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9294 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
9295 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
9296 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
9297 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9298 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
9299 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
9300
9301 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
9302 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9303 attributesFunc->setIsStatic( false );
9304 functions << attributesFunc;
9305 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
9306 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9307 representAttributesFunc->setIsStatic( false );
9308 functions << representAttributesFunc;
9309
9310 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
9311 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9312 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9313 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9314 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9315 validateFeature->setIsStatic( false );
9316 functions << validateFeature;
9317
9318 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
9319 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
9320 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9321 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9322 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9323 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9324 validateAttribute->setIsStatic( false );
9325 functions << validateAttribute;
9326
9328 QStringLiteral( "maptip" ),
9329 -1,
9330 fcnFeatureMaptip,
9331 QStringLiteral( "Record and Attributes" ),
9332 QString(),
9333 false,
9334 QSet<QString>()
9335 );
9336 maptipFunc->setIsStatic( false );
9337 functions << maptipFunc;
9338
9340 QStringLiteral( "display_expression" ),
9341 -1,
9342 fcnFeatureDisplayExpression,
9343 QStringLiteral( "Record and Attributes" ),
9344 QString(),
9345 false,
9346 QSet<QString>()
9347 );
9348 displayFunc->setIsStatic( false );
9349 functions << displayFunc;
9350
9352 QStringLiteral( "is_selected" ),
9353 -1,
9354 fcnIsSelected,
9355 QStringLiteral( "Record and Attributes" ),
9356 QString(),
9357 false,
9358 QSet<QString>()
9359 );
9360 isSelectedFunc->setIsStatic( false );
9361 functions << isSelectedFunc;
9362
9363 functions
9365 QStringLiteral( "num_selected" ),
9366 -1,
9367 fcnNumSelected,
9368 QStringLiteral( "Record and Attributes" ),
9369 QString(),
9370 false,
9371 QSet<QString>()
9372 );
9373
9374 functions
9376 QStringLiteral( "sqlite_fetch_and_increment" ),
9378 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
9379 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
9380 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
9381 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
9382 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
9383 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
9384 fcnSqliteFetchAndIncrement,
9385 QStringLiteral( "Record and Attributes" )
9386 );
9387
9388 // **Fields and Values** functions
9389 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
9390
9391 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9392 {
9393 Q_UNUSED( context )
9394 if ( node->args()->count() == 1 )
9395 {
9396 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9397 if ( colRef )
9398 {
9399 return true;
9400 }
9401 else
9402 {
9403 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9404 return false;
9405 }
9406 }
9407 else if ( node->args()->count() == 2 )
9408 {
9409 return true;
9410 }
9411 else
9412 {
9413 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9414 return false;
9415 }
9416 }
9417 );
9418
9419 functions << representValueFunc;
9420
9421 // **General** functions
9422 functions
9423 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9424 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
9425 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
9426 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
9428 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9429 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
9430 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
9431 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
9433 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
9434 fcnMimeType, QStringLiteral( "General" ) )
9435 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9436 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
9437 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
9438
9439 // **var** function
9440 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
9441 varFunction->setIsStaticFunction(
9442 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9443 {
9444 /* A variable node is static if it has a static name and the name can be found at prepare
9445 * time and is tagged with isStatic.
9446 * It is not static if a variable is set during iteration or not tagged isStatic.
9447 * (e.g. geom_part variable)
9448 */
9449 if ( node->args()->count() > 0 )
9450 {
9451 QgsExpressionNode *argNode = node->args()->at( 0 );
9452
9453 if ( !argNode->isStatic( parent, context ) )
9454 return false;
9455
9456 const QString varName = argNode->eval( parent, context ).toString();
9457 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
9458 return false;
9459
9460 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9461 return scope ? scope->isStatic( varName ) : false;
9462 }
9463 return false;
9464 }
9465 );
9466 varFunction->setUsesGeometryFunction(
9467 []( const QgsExpressionNodeFunction * node ) -> bool
9468 {
9469 if ( node && node->args()->count() > 0 )
9470 {
9471 QgsExpressionNode *argNode = node->args()->at( 0 );
9472 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9473 {
9474 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
9475 return true;
9476 }
9477 }
9478 return false;
9479 }
9480 );
9481
9482 functions
9483 << varFunction;
9484
9485 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9486 evalTemplateFunction->setIsStaticFunction(
9487 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9488 {
9489 if ( node->args()->count() > 0 )
9490 {
9491 QgsExpressionNode *argNode = node->args()->at( 0 );
9492
9493 if ( argNode->isStatic( parent, context ) )
9494 {
9495 QString expString = argNode->eval( parent, context ).toString();
9496
9497 QgsExpression e( expString );
9498
9499 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9500 return true;
9501 }
9502 }
9503
9504 return false;
9505 } );
9506 functions << evalTemplateFunction;
9507
9508 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9509 evalFunc->setIsStaticFunction(
9510 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9511 {
9512 if ( node->args()->count() > 0 )
9513 {
9514 QgsExpressionNode *argNode = node->args()->at( 0 );
9515
9516 if ( argNode->isStatic( parent, context ) )
9517 {
9518 QString expString = argNode->eval( parent, context ).toString();
9519
9520 QgsExpression e( expString );
9521
9522 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9523 return true;
9524 }
9525 }
9526
9527 return false;
9528 } );
9529
9530 functions << evalFunc;
9531
9532 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9533 attributeFunc->setIsStaticFunction(
9534 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9535 {
9536 const QList< QgsExpressionNode *> argList = node->args()->list();
9537 for ( QgsExpressionNode *argNode : argList )
9538 {
9539 if ( !argNode->isStatic( parent, context ) )
9540 return false;
9541 }
9542
9543 if ( node->args()->count() == 1 )
9544 {
9545 // not static -- this is the variant which uses the current feature taken direct from the expression context
9546 return false;
9547 }
9548
9549 return true;
9550 } );
9551 functions << attributeFunc;
9552
9553 functions
9554 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9556 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9557 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9558
9559 // functions for arrays
9562 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9563 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9564 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9565 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9566 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9567 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9568 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9569 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9570 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9571 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9572 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9573 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9574 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9575 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9576 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9577 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9578 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9579 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9580 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9581 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9582 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9583 << 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 )
9584 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9585 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9586 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9587 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9588 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9589 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9590 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9591 << 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" ) )
9592 << 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" ) )
9593 << 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" ) )
9594 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9595
9596 //functions for maps
9597 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9598 << 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" ) )
9599 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9600 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9601 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9602 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9603 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9604 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9605 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9606 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9607 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9608 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9609 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9610 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9611 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9612 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9613 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9614 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9615 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9616 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9617 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9618
9619 ;
9620
9622
9623 //QgsExpression has ownership of all built-in functions
9624 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9625 {
9626 *sOwnedFunctions() << func;
9627 *sBuiltinFunctions() << func->name();
9628 sBuiltinFunctions()->append( func->aliases() );
9629 }
9630 }
9631 return functions;
9632}
9633
9634bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9635{
9636 int fnIdx = functionIndex( function->name() );
9637 if ( fnIdx != -1 )
9638 {
9639 return false;
9640 }
9641
9642 QMutexLocker locker( &sFunctionsMutex );
9643 sFunctions()->append( function );
9644 if ( transferOwnership )
9645 sOwnedFunctions()->append( function );
9646
9647 return true;
9648}
9649
9650bool QgsExpression::unregisterFunction( const QString &name )
9651{
9652 // You can never override the built in functions.
9653 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9654 {
9655 return false;
9656 }
9657 int fnIdx = functionIndex( name );
9658 if ( fnIdx != -1 )
9659 {
9660 QMutexLocker locker( &sFunctionsMutex );
9661 sFunctions()->removeAt( fnIdx );
9662 sFunctionIndexMap.clear();
9663 return true;
9664 }
9665 return false;
9666}
9667
9669{
9670 qDeleteAll( *sOwnedFunctions() );
9671 sOwnedFunctions()->clear();
9673
9674const QStringList &QgsExpression::BuiltinFunctions()
9675{
9676 if ( sBuiltinFunctions()->isEmpty() )
9677 {
9678 Functions(); // this method builds the gmBuiltinFunctions as well
9679 }
9680 return *sBuiltinFunctions();
9682
9684 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9685 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9686 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9687 QStringLiteral( "Arrays" ) )
9688{
9689
9690}
9691
9693{
9694 bool isStatic = false;
9695
9696 QgsExpressionNode::NodeList *args = node->args();
9698 if ( args->count() < 2 )
9699 return false;
9700
9701 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9702 {
9703 isStatic = true;
9704 }
9705 return isStatic;
9706}
9707
9709{
9710 Q_UNUSED( node )
9711 QVariantList result;
9712
9713 if ( args->count() < 2 )
9714 // error
9715 return result;
9716
9717 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9718
9719 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9720 std::unique_ptr< QgsExpressionContext > tempContext;
9721 if ( !subContext )
9722 {
9723 tempContext = std::make_unique< QgsExpressionContext >();
9724 subContext = tempContext.get();
9725 }
9726
9728 subContext->appendScope( subScope );
9729
9730 int i = 0;
9731 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
9732 {
9733 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9734 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), i, true ) );
9735 result << args->at( 1 )->eval( parent, subContext );
9736 }
9737
9738 if ( context )
9739 delete subContext->popScope();
9740
9741 return result;
9742}
9743
9744QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9746 // This is a dummy function, all the real handling is in run
9747 Q_UNUSED( values )
9748 Q_UNUSED( context )
9749 Q_UNUSED( parent )
9750 Q_UNUSED( node )
9751
9752 Q_ASSERT( false );
9753 return QVariant();
9754}
9755
9757{
9758 QgsExpressionNode::NodeList *args = node->args();
9759
9760 if ( args->count() < 2 )
9761 // error
9762 return false;
9763
9764 args->at( 0 )->prepare( parent, context );
9765
9766 QgsExpressionContext subContext;
9767 if ( context )
9768 subContext = *context;
9771 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9772 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), QVariant(), true ) );
9773 subContext.appendScope( subScope );
9774
9775 args->at( 1 )->prepare( parent, &subContext );
9776
9777 return true;
9778}
9781 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9782 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9783 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9784 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9785 QStringLiteral( "Arrays" ) )
9786{
9787
9788}
9789
9791{
9792 bool isStatic = false;
9793
9794 QgsExpressionNode::NodeList *args = node->args();
9796 if ( args->count() < 2 )
9797 return false;
9798
9799 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9800 {
9801 isStatic = true;
9802 }
9803 return isStatic;
9804}
9805
9807{
9808 Q_UNUSED( node )
9809 QVariantList result;
9810
9811 if ( args->count() < 2 )
9812 // error
9813 return result;
9814
9815 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9816
9817 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9818 std::unique_ptr< QgsExpressionContext > tempContext;
9819 if ( !subContext )
9820 {
9821 tempContext = std::make_unique< QgsExpressionContext >();
9822 subContext = tempContext.get();
9823 }
9824
9826 subContext->appendScope( subScope );
9827
9828 int limit = 0;
9829 if ( args->count() >= 3 )
9830 {
9831 const QVariant limitVar = args->at( 2 )->eval( parent, context );
9832
9833 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
9834 {
9835 limit = limitVar.toInt();
9836 }
9837 else
9838 {
9839 return result;
9840 }
9841 }
9842
9843 for ( const QVariant &value : array )
9844 {
9845 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
9846 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
9847 {
9848 result << value;
9849
9850 if ( limit > 0 && limit == result.size() )
9851 break;
9852 }
9853 }
9854
9855 if ( context )
9856 delete subContext->popScope();
9857
9858 return result;
9859}
9860
9861QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9863 // This is a dummy function, all the real handling is in run
9864 Q_UNUSED( values )
9865 Q_UNUSED( context )
9866 Q_UNUSED( parent )
9867 Q_UNUSED( node )
9868
9869 Q_ASSERT( false );
9870 return QVariant();
9871}
9872
9874{
9875 QgsExpressionNode::NodeList *args = node->args();
9876
9877 if ( args->count() < 2 )
9878 // error
9879 return false;
9880
9881 args->at( 0 )->prepare( parent, context );
9882
9883 QgsExpressionContext subContext;
9884 if ( context )
9885 subContext = *context;
9886
9888 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9889 subContext.appendScope( subScope );
9890
9891 args->at( 1 )->prepare( parent, &subContext );
9892
9893 return true;
9896 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
9897 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
9898 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
9899 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9900 QStringLiteral( "General" ) )
9901{
9902
9903}
9904
9906{
9907 bool isStatic = false;
9908
9909 QgsExpressionNode::NodeList *args = node->args();
9910
9911 if ( args->count() < 3 )
9912 return false;
9913
9914 // We only need to check if the node evaluation is static, if both - name and value - are static.
9915 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9916 {
9917 QVariant name = args->at( 0 )->eval( parent, context );
9918 QVariant value = args->at( 1 )->eval( parent, context );
9920 // Temporarily append a new scope to provide the variable
9921 appendTemporaryVariable( context, name.toString(), value );
9922 if ( args->at( 2 )->isStatic( parent, context ) )
9923 isStatic = true;
9924 popTemporaryVariable( context );
9925 }
9926
9927 return isStatic;
9928}
9929
9931{
9932 Q_UNUSED( node )
9933 QVariant result;
9934
9935 if ( args->count() < 3 )
9936 // error
9937 return result;
9938
9939 QVariant name = args->at( 0 )->eval( parent, context );
9940 QVariant value = args->at( 1 )->eval( parent, context );
9941
9942 const QgsExpressionContext *updatedContext = context;
9943 std::unique_ptr< QgsExpressionContext > tempContext;
9944 if ( !updatedContext )
9945 {
9946 tempContext = std::make_unique< QgsExpressionContext >();
9947 updatedContext = tempContext.get();
9949
9950 appendTemporaryVariable( updatedContext, name.toString(), value );
9951 result = args->at( 2 )->eval( parent, updatedContext );
9952
9953 if ( context )
9954 popTemporaryVariable( updatedContext );
9955
9956 return result;
9957}
9958
9959QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9961 // This is a dummy function, all the real handling is in run
9962 Q_UNUSED( values )
9963 Q_UNUSED( context )
9964 Q_UNUSED( parent )
9965 Q_UNUSED( node )
9966
9967 Q_ASSERT( false );
9968 return QVariant();
9969}
9970
9972{
9973 QgsExpressionNode::NodeList *args = node->args();
9974
9975 if ( args->count() < 3 )
9976 // error
9977 return false;
9978
9979 QVariant name = args->at( 0 )->prepare( parent, context );
9980 QVariant value = args->at( 1 )->prepare( parent, context );
9981
9982 const QgsExpressionContext *updatedContext = context;
9983 std::unique_ptr< QgsExpressionContext > tempContext;
9984 if ( !updatedContext )
9985 {
9986 tempContext = std::make_unique< QgsExpressionContext >();
9987 updatedContext = tempContext.get();
9988 }
9989
9990 appendTemporaryVariable( updatedContext, name.toString(), value );
9991 args->at( 2 )->prepare( parent, updatedContext );
9992
9993 if ( context )
9994 popTemporaryVariable( updatedContext );
9995
9996 return true;
9997}
9998
9999void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10000{
10001 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10002 delete updatedContext->popScope();
10003}
10004
10005void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10006{
10009
10010 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10011 updatedContext->appendScope( scope );
10012}
@ Left
Buffer to left of line.
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3131
@ 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:2031
@ Bevel
Use beveled joins.
@ Round
Use rounded joins.
@ Miter
Use mitered joins.
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:5600
@ 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:2018
@ 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:5477
@ 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:3116
@ 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:2077
@ 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:43
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.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
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.
Class for doing transforms between two map 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.
A 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 it 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.
Class for 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.
This class 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:740
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:2792
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
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:76
QString name
Definition qgsmaplayer.h:80
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:83
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:79
QgsLayerMetadata metadata
Definition qgsmaplayer.h:82
Qgis::LayerType type
Definition qgsmaplayer.h:86
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:89
Implementation of GeometrySimplifier 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)
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.
A class to represent 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:694
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:706
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.
c++ 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.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
Triangle geometry type.
Definition qgstriangle.h:33
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 data sets.
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)
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
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:121
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6643
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6642
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6107
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6066
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)
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