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