QGIS API Documentation 3.41.0-Master (d2aaa9c6e02)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
17#include <random>
18
20#include "qgscoordinateutils.h"
22#include "qgsexpressionutils.h"
24#include "qgsexiftools.h"
25#include "qgsfeaturerequest.h"
26#include "qgsgeos.h"
27#include "qgsstringutils.h"
28#include "qgsmultipoint.h"
29#include "qgsgeometryutils.h"
30#include "qgshstoreutils.h"
31#include "qgsmultilinestring.h"
32#include "qgslinestring.h"
33#include "qgscurvepolygon.h"
35#include "qgspolygon.h"
36#include "qgstriangle.h"
37#include "qgscurve.h"
38#include "qgsregularpolygon.h"
39#include "qgsquadrilateral.h"
40#include "qgsvariantutils.h"
41#include "qgsogcutils.h"
42#include "qgsdistancearea.h"
43#include "qgsgeometryengine.h"
45#include "qgssymbollayerutils.h"
46#include "qgsstyle.h"
47#include "qgsexception.h"
48#include "qgsmessagelog.h"
49#include "qgsrasterlayer.h"
50#include "qgsvectorlayer.h"
51#include "qgsvectorlayerutils.h"
52#include "qgsrasterbandstats.h"
53#include "qgscolorramp.h"
55#include "qgsfieldformatter.h"
57#include "qgsproviderregistry.h"
58#include "sqlite3.h"
59#include "qgstransaction.h"
60#include "qgsthreadingutils.h"
61#include "qgsapplication.h"
62#include "qgis.h"
64#include "qgsunittypes.h"
65#include "qgsspatialindex.h"
66#include "qgscolorrampimpl.h"
67
68#include <QMimeDatabase>
69#include <QProcessEnvironment>
70#include <QCryptographicHash>
71#include <QRegularExpression>
72#include <QUuid>
73#include <QUrlQuery>
74
75typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
76
78Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
80
83Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
84
85const QString QgsExpressionFunction::helpText() const
86{
87 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
88}
89
91{
92 Q_UNUSED( node )
93 // evaluate arguments
94 QVariantList argValues;
95 if ( args )
96 {
97 int arg = 0;
98 const QList< QgsExpressionNode * > argList = args->list();
99 for ( QgsExpressionNode *n : argList )
100 {
101 QVariant v;
102 if ( lazyEval() )
103 {
104 // Pass in the node for the function to eval as it needs.
105 v = QVariant::fromValue( n );
106 }
107 else
108 {
109 v = n->eval( parent, context );
111 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
112 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
113 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
114 }
115 argValues.append( v );
116 arg++;
117 }
118 }
119
120 return func( argValues, context, parent, node );
121}
122
124{
125 Q_UNUSED( node )
126 return true;
127}
128
130{
131 return QStringList();
132}
133
135{
136 Q_UNUSED( parent )
137 Q_UNUSED( context )
138 Q_UNUSED( node )
139 return false;
140}
141
143{
144 Q_UNUSED( parent )
145 Q_UNUSED( context )
146 Q_UNUSED( node )
147 return true;
148}
149
151{
152 Q_UNUSED( node )
153 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
154}
155
157{
158 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
159}
160
162{
163 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
164}
165
167{
168 return mHandlesNull;
169}
170
171// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
174 FcnEval fcn,
175 const QString &group,
176 const QString &helpText,
177 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
178 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
179 bool lazyEval,
180 const QStringList &aliases,
181 bool handlesNull )
182 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
183 , mFnc( fcn )
184 , mAliases( aliases )
185 , mUsesGeometry( false )
186 , mUsesGeometryFunc( usesGeometry )
187 , mReferencedColumnsFunc( referencedColumns )
188{
189}
191
193{
194 return mAliases;
195}
196
198{
199 if ( mUsesGeometryFunc )
200 return mUsesGeometryFunc( node );
201 else
202 return mUsesGeometry;
203}
204
205void QgsStaticExpressionFunction::setUsesGeometryFunction( const std::function<bool ( const QgsExpressionNodeFunction * )> &usesGeometry )
206{
207 mUsesGeometryFunc = usesGeometry;
208}
209
211{
212 if ( mReferencedColumnsFunc )
213 return mReferencedColumnsFunc( node );
214 else
215 return mReferencedColumns;
216}
217
219{
220 if ( mIsStaticFunc )
221 return mIsStaticFunc( node, parent, context );
222 else
223 return mIsStatic;
224}
225
227{
228 if ( mPrepareFunc )
229 return mPrepareFunc( node, parent, context );
230
231 return true;
232}
233
235{
236 mIsStaticFunc = isStatic;
237}
238
240{
241 mIsStaticFunc = nullptr;
242 mIsStatic = isStatic;
243}
244
245void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
246{
247 mPrepareFunc = prepareFunc;
248}
249
251{
252 if ( node && node->args() )
253 {
254 const QList< QgsExpressionNode * > argList = node->args()->list();
255 for ( QgsExpressionNode *argNode : argList )
256 {
257 if ( !argNode->isStatic( parent, context ) )
258 return false;
259 }
260 }
261
262 return true;
263}
264
265static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
266{
267 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
268 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
269 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
270
271 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
272 return QVariant();
273
274 QVariantList array;
275 int length = 1;
276
277 array << start;
278 double current = start + step;
279 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
280 {
281 array << current;
282 current += step;
283 length++;
284 }
285
286 return array;
287}
288
289static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
290{
291 if ( !context )
292 return QVariant();
293
294 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
295
296 if ( name == QLatin1String( "feature" ) )
297 {
298 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
299 }
300 else if ( name == QLatin1String( "id" ) )
301 {
302 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
303 }
304 else if ( name == QLatin1String( "geometry" ) )
305 {
306 if ( !context->hasFeature() )
307 return QVariant();
308
309 const QgsFeature feature = context->feature();
310 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
311 }
312 else
313 {
314 return context->variable( name );
315 }
316}
317
318static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
319{
320 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
321 return QgsExpression::replaceExpressionText( templateString, context );
322}
323
324static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
325{
326 if ( !context )
327 return QVariant();
328
329 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
330 QgsExpression expression( expString );
331 return expression.evaluate( context );
332}
333
334static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
335{
336 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
337 return QVariant( std::sqrt( x ) );
338}
339
340static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
341{
342 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
343 return QVariant( std::fabs( val ) );
344}
345
346static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
347{
348 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
349 return ( deg * M_PI ) / 180;
350}
351static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
352{
353 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
354 return ( 180 * rad ) / M_PI;
355}
356static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
357{
358 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
359 return QVariant( std::sin( x ) );
360}
361static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
362{
363 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
364 return QVariant( std::cos( x ) );
365}
366static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
367{
368 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
369 return QVariant( std::tan( x ) );
370}
371static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
372{
373 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
374 return QVariant( std::asin( x ) );
375}
376static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
377{
378 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
379 return QVariant( std::acos( x ) );
380}
381static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
382{
383 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
384 return QVariant( std::atan( x ) );
385}
386static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
387{
388 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
389 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
390 return QVariant( std::atan2( y, x ) );
391}
392static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
393{
394 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
395 return QVariant( std::exp( x ) );
396}
397static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
398{
399 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
400 if ( x <= 0 )
401 return QVariant();
402 return QVariant( std::log( x ) );
403}
404static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
405{
406 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
407 if ( x <= 0 )
408 return QVariant();
409 return QVariant( log10( x ) );
410}
411static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
412{
413 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
414 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
415 if ( x <= 0 || b <= 0 )
416 return QVariant();
417 return QVariant( std::log( x ) / std::log( b ) );
418}
419static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
420{
421 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
422 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
423 if ( max < min )
424 return QVariant();
425
426 std::random_device rd;
427 std::mt19937_64 generator( rd() );
428
429 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
430 {
431 quint32 seed;
432 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
433 {
434 // if seed can be converted to int, we use as is
435 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
436 }
437 else
438 {
439 // if not, we hash string representation to int
440 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
441 std::hash<std::string> hasher;
442 seed = hasher( seedStr.toStdString() );
443 }
444 generator.seed( seed );
445 }
446
447 // Return a random double in the range [min, max] (inclusive)
448 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
449 return QVariant( min + f * ( max - min ) );
450}
451static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
452{
453 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
454 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
455 if ( max < min )
456 return QVariant();
457
458 std::random_device rd;
459 std::mt19937_64 generator( rd() );
460
461 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
462 {
463 quint32 seed;
464 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
465 {
466 // if seed can be converted to int, we use as is
467 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
468 }
469 else
470 {
471 // if not, we hash string representation to int
472 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
473 std::hash<std::string> hasher;
474 seed = hasher( seedStr.toStdString() );
475 }
476 generator.seed( seed );
477 }
478
479 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
480 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
481 return QVariant( randomInteger );
482
483 // Prevent wrong conversion of QVariant. See #36412
484 return QVariant( int( randomInteger ) );
485}
486
487static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
488{
489 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
490 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
491 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
492 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
493 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
494
495 if ( domainMin >= domainMax )
496 {
497 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
498 return QVariant();
499 }
500
501 // outside of domain?
502 if ( val >= domainMax )
503 {
504 return rangeMax;
505 }
506 else if ( val <= domainMin )
507 {
508 return rangeMin;
509 }
510
511 // calculate linear scale
512 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
513 double c = rangeMin - ( domainMin * m );
514
515 // Return linearly scaled value
516 return QVariant( m * val + c );
517}
518
519static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
520{
521 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
522 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
523 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
524 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
525 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
526 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
527
528 if ( domainMin >= domainMax )
529 {
530 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
531 return QVariant();
532 }
533 if ( exponent <= 0 )
534 {
535 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
536 return QVariant();
537 }
538
539 // outside of domain?
540 if ( val >= domainMax )
541 {
542 return rangeMax;
543 }
544 else if ( val <= domainMin )
545 {
546 return rangeMin;
547 }
548
549 // Return polynomially scaled value
550 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
551}
552
553static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
554{
555 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
556 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
557 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
558 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
559 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
560 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
561
562 if ( domainMin >= domainMax )
563 {
564 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
565 return QVariant();
566 }
567 if ( exponent <= 0 )
568 {
569 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
570 return QVariant();
571 }
572
573 // outside of domain?
574 if ( val >= domainMax )
575 {
576 return rangeMax;
577 }
578 else if ( val <= domainMin )
579 {
580 return rangeMin;
581 }
582
583 // Return exponentially scaled value
584 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
585 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
586}
587
588static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
589{
590 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
591 double maxVal = std::numeric_limits<double>::quiet_NaN();
592 for ( const QVariant &val : values )
593 {
594 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
595 if ( std::isnan( maxVal ) )
596 {
597 maxVal = testVal;
598 }
599 else if ( !std::isnan( testVal ) )
600 {
601 maxVal = std::max( maxVal, testVal );
602 }
603 }
604
605 if ( !std::isnan( maxVal ) )
606 {
607 result = QVariant( maxVal );
608 }
609 return result;
610}
611
612static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
613{
614 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
615 double minVal = std::numeric_limits<double>::quiet_NaN();
616 for ( const QVariant &val : values )
617 {
618 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
619 if ( std::isnan( minVal ) )
620 {
621 minVal = testVal;
622 }
623 else if ( !std::isnan( testVal ) )
624 {
625 minVal = std::min( minVal, testVal );
626 }
627 }
628
629 if ( !std::isnan( minVal ) )
630 {
631 result = QVariant( minVal );
632 }
633 return result;
634}
635
636static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
637{
638 //lazy eval, so we need to evaluate nodes now
639
640 //first node is layer id or name
641 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
643 QVariant value = node->eval( parent, context );
645
646 // TODO this expression function is NOT thread safe
648 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
650 if ( !vl )
651 {
652 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
653 return QVariant();
654 }
655
656 // second node is aggregate type
657 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
659 value = node->eval( parent, context );
661 bool ok = false;
662 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
663 if ( !ok )
664 {
665 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
666 return QVariant();
667 }
668
669 // third node is subexpression (or field name)
670 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
672 QString subExpression = node->dump();
673
675 //optional forth node is filter
676 if ( values.count() > 3 )
677 {
678 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
680 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
681 if ( !nl || nl->value().isValid() )
682 parameters.filter = node->dump();
683 }
684
685 //optional fifth node is concatenator
686 if ( values.count() > 4 )
687 {
688 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
690 value = node->eval( parent, context );
692 parameters.delimiter = value.toString();
693 }
694
695 //optional sixth node is order by
696 QString orderBy;
697 if ( values.count() > 5 )
698 {
699 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
701 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
702 if ( !nl || nl->value().isValid() )
703 {
704 orderBy = node->dump();
705 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
706 }
707 }
708
709 QString aggregateError;
710 QVariant result;
711 if ( context )
712 {
713 QString cacheKey;
714 QgsExpression subExp( subExpression );
715 QgsExpression filterExp( parameters.filter );
716
717 const QSet< QString > filterVars = filterExp.referencedVariables();
718 const QSet< QString > subExpVars = subExp.referencedVariables();
719 QSet<QString> allVars = filterVars + subExpVars;
720
721 bool isStatic = true;
722 if ( filterVars.contains( QStringLiteral( "parent" ) )
723 || filterVars.contains( QString() )
724 || subExpVars.contains( QStringLiteral( "parent" ) )
725 || subExpVars.contains( QString() ) )
726 {
727 isStatic = false;
728 }
729 else
730 {
731 for ( const QString &varName : allVars )
732 {
733 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
734 if ( scope && !scope->isStatic( varName ) )
735 {
736 isStatic = false;
737 break;
738 }
739 }
740 }
741
742 if ( isStatic && ! parameters.orderBy.isEmpty() )
743 {
744 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
745 {
746 const QgsExpression &orderByExpression { orderByClause.expression() };
747 if ( orderByExpression.referencedVariables().contains( QStringLiteral( "parent" ) ) || orderByExpression.referencedVariables().contains( QString() ) )
748 {
749 isStatic = false;
750 break;
751 }
752 }
753 }
754
755 if ( !isStatic )
756 {
757 bool ok = false;
758 const QString contextHash = context->uniqueHash( ok, allVars );
759 if ( ok )
760 {
761 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
762 orderBy, contextHash );
763 }
764 }
765 else
766 {
767 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
768 }
769
770 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
771 {
772 return context->cachedValue( cacheKey );
773 }
774
775 QgsExpressionContext subContext( *context );
777 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
778 subContext.appendScope( subScope );
779 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
780
781 if ( ok && !cacheKey.isEmpty() )
782 {
783 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
784 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
785 // associated with it's calculation!
786 context->setCachedValue( cacheKey, result );
787 }
788 }
789 else
790 {
791 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
792 }
793 if ( !ok )
794 {
795 if ( !aggregateError.isEmpty() )
796 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
797 else
798 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
799 return QVariant();
800 }
801
802 return result;
803}
804
805static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
806{
807 if ( !context )
808 {
809 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
810 return QVariant();
811 }
812
813 // first step - find current layer
814
815 // TODO this expression function is NOT thread safe
817 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
819 if ( !vl )
820 {
821 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
822 return QVariant();
823 }
824
825 //lazy eval, so we need to evaluate nodes now
826
827 //first node is relation name
828 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
830 QVariant value = node->eval( parent, context );
832 QString relationId = value.toString();
833 // check relation exists
834 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
835 if ( !relation.isValid() || relation.referencedLayer() != vl )
836 {
837 // check for relations by name
838 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
839 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
840 {
841 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
842 return QVariant();
843 }
844 else
845 {
846 relation = relations.at( 0 );
847 }
848 }
849
850 QgsVectorLayer *childLayer = relation.referencingLayer();
851
852 // second node is aggregate type
853 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
855 value = node->eval( parent, context );
857 bool ok = false;
858 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
859 if ( !ok )
860 {
861 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
862 return QVariant();
863 }
864
865 //third node is subexpression (or field name)
866 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
868 QString subExpression = node->dump();
869
870 //optional fourth node is concatenator
872 if ( values.count() > 3 )
873 {
874 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
876 value = node->eval( parent, context );
878 parameters.delimiter = value.toString();
879 }
880
881 //optional fifth node is order by
882 QString orderBy;
883 if ( values.count() > 4 )
884 {
885 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
887 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
888 if ( !nl || nl->value().isValid() )
889 {
890 orderBy = node->dump();
891 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
892 }
893 }
894
895 if ( !context->hasFeature() )
896 return QVariant();
897 QgsFeature f = context->feature();
898
899 parameters.filter = relation.getRelatedFeaturesFilter( f );
900
901 const QString cacheKey = QStringLiteral( "relagg:%1%:%2:%3:%4:%5:%6" ).arg( relationId, vl->id(),
902 QString::number( static_cast< int >( aggregate ) ),
903 subExpression,
904 parameters.filter,
905 orderBy );
906 if ( context->hasCachedValue( cacheKey ) )
907 return context->cachedValue( cacheKey );
908
909 QVariant result;
910 ok = false;
911
912
913 QgsExpressionContext subContext( *context );
914 QString error;
915 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
916
917 if ( !ok )
918 {
919 if ( !error.isEmpty() )
920 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
921 else
922 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
923 return QVariant();
924 }
925
926 // cache value
927 context->setCachedValue( cacheKey, result );
928 return result;
929}
930
931
932static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
933{
934 if ( !context )
935 {
936 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
937 return QVariant();
938 }
939
940 // first step - find current layer
941
942 // TODO this expression function is NOT thread safe
944 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
946 if ( !vl )
947 {
948 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
949 return QVariant();
950 }
951
952 //lazy eval, so we need to evaluate nodes now
953
954 //first node is subexpression (or field name)
955 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
957 QString subExpression = node->dump();
958
959 //optional second node is group by
960 QString groupBy;
961 if ( values.count() > 1 )
962 {
963 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
965 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
966 if ( !nl || nl->value().isValid() )
967 groupBy = node->dump();
968 }
969
970 //optional third node is filter
971 if ( values.count() > 2 )
972 {
973 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
975 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
976 if ( !nl || nl->value().isValid() )
977 parameters.filter = node->dump();
978 }
979
980 //optional order by node, if supported
981 QString orderBy;
982 if ( orderByPos >= 0 && values.count() > orderByPos )
983 {
984 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
986 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
987 if ( !nl || nl->value().isValid() )
988 {
989 orderBy = node->dump();
990 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
991 }
992 }
993
994 // build up filter with group by
995
996 // find current group by value
997 if ( !groupBy.isEmpty() )
998 {
999 QgsExpression groupByExp( groupBy );
1000 QVariant groupByValue = groupByExp.evaluate( context );
1001 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
1002 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
1003 QgsExpression::quotedValue( groupByValue ) );
1004 if ( !parameters.filter.isEmpty() )
1005 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
1006 else
1007 parameters.filter = groupByClause;
1008 }
1009
1010 QgsExpression subExp( subExpression );
1011 QgsExpression filterExp( parameters.filter );
1012
1013 bool isStatic = true;
1014 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1015 for ( const QString &varName : refVars )
1016 {
1017 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1018 if ( scope && !scope->isStatic( varName ) )
1019 {
1020 isStatic = false;
1021 break;
1022 }
1023 }
1024
1025 QString cacheKey;
1026 if ( !isStatic )
1027 {
1028 bool ok = false;
1029 const QString contextHash = context->uniqueHash( ok, refVars );
1030 if ( ok )
1031 {
1032 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1033 orderBy, contextHash );
1034 }
1035 }
1036 else
1037 {
1038 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1039 }
1040
1041 if ( context->hasCachedValue( cacheKey ) )
1042 return context->cachedValue( cacheKey );
1043
1044 QVariant result;
1045 bool ok = false;
1046
1047 QgsExpressionContext subContext( *context );
1049 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
1050 subContext.appendScope( subScope );
1051 QString error;
1052 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1053
1054 if ( !ok )
1055 {
1056 if ( !error.isEmpty() )
1057 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1058 else
1059 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1060 return QVariant();
1061 }
1062
1063 // cache value
1064 context->setCachedValue( cacheKey, result );
1065 return result;
1066}
1067
1068
1069static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1070{
1071 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1072}
1073
1074static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1075{
1076 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1077}
1078
1079static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1080{
1081 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1082}
1083
1084static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1085{
1086 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1087}
1088
1089static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1090{
1091 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1092}
1093
1094static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1095{
1096 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1097}
1098
1099static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1100{
1101 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1102}
1103
1104static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1105{
1106 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1107}
1108
1109static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1110{
1111 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1112}
1113
1114static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1115{
1116 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1117}
1118
1119static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1120{
1121 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1122}
1123
1124static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1125{
1126 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1127}
1128
1129static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1130{
1131 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1132}
1133
1134static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1135{
1136 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1137}
1138
1139static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1140{
1141 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1142}
1143
1144static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1145{
1146 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1147}
1148
1149static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1150{
1151 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1152}
1153
1154static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1155{
1156 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1157}
1158
1159static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1160{
1162
1163 //fourth node is concatenator
1164 if ( values.count() > 3 )
1165 {
1166 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1168 QVariant value = node->eval( parent, context );
1170 parameters.delimiter = value.toString();
1171 }
1172
1173 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1174}
1175
1176static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1177{
1179
1180 //fourth node is concatenator
1181 if ( values.count() > 3 )
1182 {
1183 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1185 QVariant value = node->eval( parent, context );
1187 parameters.delimiter = value.toString();
1188 }
1189
1190 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1191}
1192
1193static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1194{
1195 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1196}
1197
1198static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1199{
1200 if ( !context )
1201 return QVariant();
1202
1203 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1204 bool ok = false;
1205 if ( QgsVariantUtils::isNull( scale ) )
1206 return QVariant();
1207
1208 const double v = scale.toDouble( &ok );
1209 if ( ok )
1210 return v;
1211 return QVariant();
1212}
1213
1214static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1215{
1216 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1217 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1218 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1219
1220 // force testValue to sit inside the range specified by the min and max value
1221 if ( testValue <= minValue )
1222 {
1223 return QVariant( minValue );
1224 }
1225 else if ( testValue >= maxValue )
1226 {
1227 return QVariant( maxValue );
1228 }
1229 else
1230 {
1231 return QVariant( testValue );
1232 }
1233}
1234
1235static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1236{
1237 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1238 return QVariant( std::floor( x ) );
1239}
1240
1241static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1242{
1243 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1244 return QVariant( std::ceil( x ) );
1245}
1246
1247static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1248{
1249 const QVariant value = values.at( 0 );
1250 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1251 {
1252 return QVariant( false );
1253 }
1254 else if ( value.userType() == QMetaType::QString )
1255 {
1256 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1257 return QVariant( !value.toString().isEmpty() );
1258 }
1259 else if ( QgsExpressionUtils::isList( value ) )
1260 {
1261 return !value.toList().isEmpty();
1262 }
1263 return QVariant( value.toBool() );
1264}
1265static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1266{
1267 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1268}
1269static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1270{
1271 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1272}
1273static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1274{
1275 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1276}
1277
1278static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1279{
1280 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1281 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1282 if ( format.isEmpty() && !language.isEmpty() )
1283 {
1284 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1285 return QVariant( QDateTime() );
1286 }
1287
1288 if ( format.isEmpty() && language.isEmpty() )
1289 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1290
1291 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1292 QLocale locale = QLocale();
1293 if ( !language.isEmpty() )
1294 {
1295 locale = QLocale( language );
1296 }
1297
1298 QDateTime datetime = locale.toDateTime( datetimestring, format );
1299 if ( !datetime.isValid() )
1300 {
1301 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1302 datetime = QDateTime();
1303 }
1304 return QVariant( datetime );
1305}
1306
1307static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1308{
1309 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1310 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1311 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1312
1313 const QDate date( year, month, day );
1314 if ( !date.isValid() )
1315 {
1316 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1317 return QVariant();
1318 }
1319 return QVariant( date );
1320}
1321
1322static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1323{
1324 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1325 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1326 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1327
1328 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1329 if ( !time.isValid() )
1330 {
1331 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1332 return QVariant();
1333 }
1334 return QVariant( time );
1335}
1336
1337static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1338{
1339 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1340 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1341 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1342 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1343 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1344 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1345
1346 const QDate date( year, month, day );
1347 if ( !date.isValid() )
1348 {
1349 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1350 return QVariant();
1351 }
1352 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1353 if ( !time.isValid() )
1354 {
1355 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1356 return QVariant();
1357 }
1358 return QVariant( QDateTime( date, time ) );
1359}
1360
1361static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1362{
1363 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1364 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1365 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1366 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1367 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1368 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1369 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1370
1371 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1372}
1373
1374static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1375{
1376 for ( const QVariant &value : values )
1377 {
1378 if ( QgsVariantUtils::isNull( value ) )
1379 continue;
1380 return value;
1381 }
1382 return QVariant();
1383}
1384
1385static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1386{
1387 const QVariant val1 = values.at( 0 );
1388 const QVariant val2 = values.at( 1 );
1389
1390 if ( val1 == val2 )
1391 return QVariant();
1392 else
1393 return val1;
1394}
1395
1396static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1397{
1398 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1399 return QVariant( str.toLower() );
1400}
1401static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1402{
1403 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1404 return QVariant( str.toUpper() );
1405}
1406static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1407{
1408 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1409 QStringList elems = str.split( ' ' );
1410 for ( int i = 0; i < elems.size(); i++ )
1411 {
1412 if ( elems[i].size() > 1 )
1413 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1414 }
1415 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1416}
1417
1418static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1419{
1420 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1421 return QVariant( str.trimmed() );
1422}
1423
1424static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1425{
1426 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1427
1428 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1429
1430 const QRegularExpression re( QStringLiteral( "^([%1]*)" ).arg( QRegularExpression::escape( characters ) ) );
1431 str.replace( re, QString() );
1432 return QVariant( str );
1433}
1434
1435static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1436{
1437 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1438
1439 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1440
1441 const QRegularExpression re( QStringLiteral( "([%1]*)$" ).arg( QRegularExpression::escape( characters ) ) );
1442 str.replace( re, QString() );
1443 return QVariant( str );
1444}
1445
1446static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1447{
1448 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1449 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1450 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1451}
1452
1453static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1454{
1455 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1456 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1457 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1458}
1459
1460static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1461{
1462 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1463 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1464 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1465 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1466}
1467
1468static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1469{
1470 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1471 return QVariant( QgsStringUtils::soundex( string ) );
1472}
1473
1474static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1475{
1476 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1477 return QVariant( QString( character ) );
1478}
1479
1480static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1481{
1482 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1483
1484 if ( value.isEmpty() )
1485 {
1486 return QVariant();
1487 }
1488
1489 int res = value.at( 0 ).unicode();
1490 return QVariant( res );
1491}
1492
1493static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1494{
1495 if ( values.length() == 2 || values.length() == 3 )
1496 {
1497 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1498 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1499
1500 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1501
1502 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1503 }
1504
1505 return QVariant();
1506}
1507
1508static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1509{
1510 // two variants, one for geometry, one for string
1511
1512 //geometry variant
1513 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1514 if ( !geom.isNull() )
1515 {
1516 if ( geom.type() == Qgis::GeometryType::Line )
1517 return QVariant( geom.length() );
1518 else
1519 return QVariant();
1520 }
1521
1522 //otherwise fall back to string variant
1523 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1524 return QVariant( str.length() );
1525}
1526
1527static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1528{
1529 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1530
1531 if ( geom.type() != Qgis::GeometryType::Line )
1532 return QVariant();
1533
1534 double totalLength = 0;
1535 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1536 {
1537 if ( const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( *it ) )
1538 {
1539 totalLength += line->length3D();
1540 }
1541 else
1542 {
1543 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1544 totalLength += segmentized->length3D();
1545 }
1546 }
1547
1548 return totalLength;
1549}
1550
1551static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1552{
1553 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1554 {
1555 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1556 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1557 QVector< QPair< QString, QString > > mapItems;
1558
1559 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1560 {
1561 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1562 }
1563
1564 // larger keys should be replaced first since they may contain whole smaller keys
1565 std::sort( mapItems.begin(),
1566 mapItems.end(),
1567 []( const QPair< QString, QString > &pair1,
1568 const QPair< QString, QString > &pair2 )
1569 {
1570 return ( pair1.first.length() > pair2.first.length() );
1571 } );
1572
1573 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1574 {
1575 str = str.replace( it->first, it->second );
1576 }
1577
1578 return QVariant( str );
1579 }
1580 else if ( values.count() == 3 )
1581 {
1582 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1583 QVariantList before;
1584 QVariantList after;
1585 bool isSingleReplacement = false;
1586
1587 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1588 {
1589 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1590 }
1591 else
1592 {
1593 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1594 }
1595
1596 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1597 {
1598 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1599 isSingleReplacement = true;
1600 }
1601 else
1602 {
1603 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1604 }
1605
1606 if ( !isSingleReplacement && before.length() != after.length() )
1607 {
1608 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1609 return QVariant();
1610 }
1611
1612 for ( int i = 0; i < before.length(); i++ )
1613 {
1614 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1615 }
1616
1617 return QVariant( str );
1618 }
1619 else
1620 {
1621 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1622 return QVariant();
1623 }
1624}
1625
1626static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1627{
1628 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1629 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1630 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1631
1632 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1633 if ( !re.isValid() )
1634 {
1635 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1636 return QVariant();
1637 }
1638 return QVariant( str.replace( re, after ) );
1639}
1640
1641static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1642{
1643 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1644 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1645
1646 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1647 if ( !re.isValid() )
1648 {
1649 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1650 return QVariant();
1651 }
1652 return QVariant( ( str.indexOf( re ) + 1 ) );
1653}
1654
1655static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1656{
1657 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1658 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1659 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1660
1661 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1662 if ( !re.isValid() )
1663 {
1664 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1665 return QVariant();
1666 }
1667
1668 QRegularExpressionMatch matches = re.match( str );
1669 if ( matches.hasMatch() )
1670 {
1671 QVariantList array;
1672 QStringList list = matches.capturedTexts();
1673
1674 // Skip the first string to only return captured groups
1675 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1676 {
1677 array += ( !( *it ).isEmpty() ) ? *it : empty;
1678 }
1679
1680 return QVariant( array );
1681 }
1682 else
1683 {
1684 return QVariant();
1685 }
1686}
1687
1688static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1689{
1690 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1691 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1692
1693 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1694 if ( !re.isValid() )
1695 {
1696 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1697 return QVariant();
1698 }
1699
1700 // extract substring
1701 QRegularExpressionMatch match = re.match( str );
1702 if ( match.hasMatch() )
1703 {
1704 // return first capture
1705 if ( match.lastCapturedIndex() > 0 )
1706 {
1707 // a capture group was present, so use that
1708 return QVariant( match.captured( 1 ) );
1709 }
1710 else
1711 {
1712 // no capture group, so using all match
1713 return QVariant( match.captured( 0 ) );
1714 }
1715 }
1716 else
1717 {
1718 return QVariant( "" );
1719 }
1720}
1721
1722static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1723{
1724 QString uuid = QUuid::createUuid().toString();
1725 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1726 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1727 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1728 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1729 return uuid;
1730}
1731
1732static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1733{
1734 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1735 return QVariant();
1736
1737 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1738 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1739
1740 int len = 0;
1741 if ( values.at( 2 ).isValid() )
1742 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1743 else
1744 len = str.size();
1745
1746 if ( from < 0 )
1747 {
1748 from = str.size() + from;
1749 if ( from < 0 )
1750 {
1751 from = 0;
1752 }
1753 }
1754 else if ( from > 0 )
1755 {
1756 //account for the fact that substr() starts at 1
1757 from -= 1;
1758 }
1759
1760 if ( len < 0 )
1761 {
1762 len = str.size() + len - from;
1763 if ( len < 0 )
1764 {
1765 len = 0;
1766 }
1767 }
1768
1769 return QVariant( str.mid( from, len ) );
1770}
1771static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1772{
1773 FEAT_FROM_CONTEXT( context, f )
1774 // TODO: handling of 64-bit feature ids?
1775 return QVariant( static_cast< int >( f.id() ) );
1776}
1777
1778static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1779{
1780 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1781 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1782 bool foundLayer = false;
1783 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1784 {
1785 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1786 if ( !layer || !layer->dataProvider() )
1787 {
1788 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1789 return QVariant();
1790 }
1791
1792 if ( bandNb < 1 || bandNb > layer->bandCount() )
1793 {
1794 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1795 return QVariant();
1796 }
1797
1798 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1799 {
1800 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1801 return QVariant();
1802 }
1803
1804 QgsPointXY point = geom.asPoint();
1805 if ( geom.isMultipart() )
1806 {
1807 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1808 if ( multiPoint.count() == 1 )
1809 {
1810 point = multiPoint[0];
1811 }
1812 else
1813 {
1814 // if the geometry contains more than one part, return an undefined value
1815 return QVariant();
1816 }
1817 }
1818
1819 double value = layer->dataProvider()->sample( point, bandNb );
1820 return std::isnan( value ) ? QVariant() : value;
1821 },
1822 foundLayer );
1823
1824 if ( !foundLayer )
1825 {
1826 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1827 return QVariant();
1828 }
1829 else
1830 {
1831 return res;
1832 }
1833}
1834
1835static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1836{
1837 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1838 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1839
1840 bool foundLayer = false;
1841 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1842 {
1843 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1844 if ( !layer || !layer->dataProvider() )
1845 {
1846 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1847 return QVariant();
1848 }
1849
1850 if ( bandNb < 1 || bandNb > layer->bandCount() )
1851 {
1852 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1853 return QVariant();
1854 }
1855
1856 if ( std::isnan( value ) )
1857 {
1858 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1859 return QVariant();
1860 }
1861
1862 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1863 {
1864 return QVariant();
1865 }
1866
1867 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1868 if ( data.isEmpty() )
1869 {
1870 return QVariant();
1871 }
1872
1873 QVariantMap result;
1874 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1875 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1876 {
1877 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1878 if ( field.isColor() || field.isRamp() )
1879 {
1880 continue;
1881 }
1882 result.insert( fields.at( idx ).name, data.at( idx ) );
1883 }
1884
1885 return result;
1886 }, foundLayer );
1887
1888 if ( !foundLayer )
1889 {
1890 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1891 return QVariant();
1892 }
1893 else
1894 {
1895 return res;
1896 }
1897}
1898
1899static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1900{
1901 if ( !context )
1902 return QVariant();
1903
1904 return context->feature();
1905}
1906
1907static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1908{
1909 QgsFeature feature;
1910 QString attr;
1911 if ( values.size() == 1 )
1912 {
1913 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1914 feature = context->feature();
1915 }
1916 else if ( values.size() == 2 )
1917 {
1918 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1919 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1920 }
1921 else
1922 {
1923 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
1924 return QVariant();
1925 }
1926
1927 return feature.attribute( attr );
1928}
1929
1930static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1931{
1932 QString table { R"html(
1933 <table>
1934 <thead>
1935 <tr><th>%1</th></tr>
1936 </thead>
1937 <tbody>
1938 <tr><td>%2</td></tr>
1939 </tbody>
1940 </table>)html" };
1941 QVariantMap dict;
1942 if ( values.size() == 1 )
1943 {
1944 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1945 }
1946 else
1947 {
1948 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
1949 return QVariant();
1950 }
1951
1952 if ( dict.isEmpty() )
1953 {
1954 return QVariant();
1955 }
1956
1957 QStringList headers;
1958 QStringList cells;
1959
1960 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1961 {
1962 headers.push_back( it.key().toHtmlEscaped() );
1963 cells.push_back( it.value().toString( ).toHtmlEscaped() );
1964 }
1965
1966 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
1967}
1968
1969static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1970{
1971 QString table { R"html(
1972 <dl>
1973 %1
1974 </dl>)html" };
1975 QVariantMap dict;
1976 if ( values.size() == 1 )
1977 {
1978 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
1979 }
1980 else
1981 {
1982 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
1983 return QVariant();
1984 }
1985
1986 if ( dict.isEmpty() )
1987 {
1988 return QVariant();
1989 }
1990
1991 QString rows;
1992
1993 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
1994 {
1995 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
1996 }
1997
1998 return table.arg( rows );
1999}
2000
2001static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2002{
2003 QVariant layer;
2004 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2005 {
2006 layer = context->variable( QStringLiteral( "layer" ) );
2007 }
2008 else
2009 {
2010 //first node is layer id or name
2011 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2013 layer = node->eval( parent, context );
2015 }
2016
2017 QgsFeature feature;
2018 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2019 {
2020 feature = context->feature();
2021 }
2022 else
2023 {
2024 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2025 }
2026
2028 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2029 if ( strength == QLatin1String( "hard" ) )
2030 {
2032 }
2033 else if ( strength == QLatin1String( "soft" ) )
2034 {
2036 }
2037
2038 bool foundLayer = false;
2039 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2040 {
2041 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2042 if ( !layer )
2043 {
2044 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2045 return QVariant();
2046 }
2047
2048 const QgsFields fields = layer->fields();
2049 bool valid = true;
2050 for ( int i = 0; i < fields.size(); i++ )
2051 {
2052 QStringList errors;
2053 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2054 if ( !valid )
2055 {
2056 break;
2057 }
2058 }
2059
2060 return valid;
2061 }, foundLayer );
2062
2063 if ( !foundLayer )
2064 {
2065 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2066 return QVariant();
2067 }
2068
2069 return res;
2070}
2071
2072static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2073{
2074 QVariant layer;
2075 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2076 {
2077 layer = context->variable( QStringLiteral( "layer" ) );
2078 }
2079 else
2080 {
2081 //first node is layer id or name
2082 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2084 layer = node->eval( parent, context );
2086 }
2087
2088 QgsFeature feature;
2089 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2090 {
2091 feature = context->feature();
2092 }
2093 else
2094 {
2095 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2096 }
2097
2099 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2100 if ( strength == QLatin1String( "hard" ) )
2101 {
2103 }
2104 else if ( strength == QLatin1String( "soft" ) )
2105 {
2107 }
2108
2109 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2110
2111 bool foundLayer = false;
2112 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2113 {
2114 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2115 if ( !layer )
2116 {
2117 return QVariant();
2118 }
2119
2120 const int fieldIndex = layer->fields().indexFromName( attributeName );
2121 if ( fieldIndex == -1 )
2122 {
2123 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2124 return QVariant();
2125 }
2126
2127 QStringList errors;
2128 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2129 return valid;
2130 }, foundLayer );
2131
2132 if ( !foundLayer )
2133 {
2134 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2135 return QVariant();
2136 }
2137
2138 return res;
2139}
2140
2141static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2142{
2143 QgsFeature feature;
2144 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2145 {
2146 feature = context->feature();
2147 }
2148 else
2149 {
2150 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2151 }
2152
2153 const QgsFields fields = feature.fields();
2154 QVariantMap result;
2155 for ( int i = 0; i < fields.count(); ++i )
2156 {
2157 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2158 }
2159 return result;
2160}
2161
2162static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2163{
2164 QgsVectorLayer *layer = nullptr;
2165 QgsFeature feature;
2166
2167 // TODO this expression function is NOT thread safe
2169 if ( values.isEmpty() )
2170 {
2171 feature = context->feature();
2172 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2173 }
2174 else if ( values.size() == 1 )
2175 {
2176 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2177 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2178 }
2179 else if ( values.size() == 2 )
2180 {
2181 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2182 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2183 }
2184 else
2185 {
2186 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2187 return QVariant();
2188 }
2190
2191 if ( !layer )
2192 {
2193 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2194 return QVariant();
2195 }
2196
2197 if ( !feature.isValid() )
2198 {
2199 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2200 return QVariant();
2201 }
2202
2203 const QgsFields fields = feature.fields();
2204 QVariantMap result;
2205 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2206 {
2207 const QString fieldName { fields.at( fieldIndex ).name() };
2208 const QVariant attributeVal = feature.attribute( fieldIndex );
2209 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2210 if ( context && context->hasCachedValue( cacheValueKey ) )
2211 {
2212 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2213 }
2214 else
2215 {
2216 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2218 QVariant cache;
2219 if ( context )
2220 {
2221 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2222
2223 if ( !context->hasCachedValue( cacheKey ) )
2224 {
2225 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2226 context->setCachedValue( cacheKey, cache );
2227 }
2228 else
2229 {
2230 cache = context->cachedValue( cacheKey );
2231 }
2232 }
2233 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2234
2235 result.insert( fields.at( fieldIndex ).name(), value );
2236
2237 if ( context )
2238 {
2239 context->setCachedValue( cacheValueKey, value );
2240 }
2241
2242 }
2243 }
2244 return result;
2245}
2246
2247static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2248{
2249 QgsVectorLayer *layer = nullptr;
2250 QgsFeature feature;
2251 bool evaluate = true;
2252
2253 // TODO this expression function is NOT thread safe
2255 if ( values.isEmpty() )
2256 {
2257 feature = context->feature();
2258 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2259 }
2260 else if ( values.size() == 1 )
2261 {
2262 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2263 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2264 }
2265 else if ( values.size() == 2 )
2266 {
2267 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2268 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2269 }
2270 else if ( values.size() == 3 )
2271 {
2272 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2273 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2274 evaluate = values.value( 2 ).toBool();
2275 }
2276 else
2277 {
2278 if ( isMaptip )
2279 {
2280 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2281 }
2282 else
2283 {
2284 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2285 }
2286 return QVariant();
2287 }
2288
2289 if ( !layer )
2290 {
2291 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2292 return QVariant( );
2293 }
2295
2296 if ( !feature.isValid() )
2297 {
2298 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2299 return QVariant( );
2300 }
2301
2302 if ( ! evaluate )
2303 {
2304 if ( isMaptip )
2305 {
2306 return layer->mapTipTemplate();
2307 }
2308 else
2309 {
2310 return layer->displayExpression();
2311 }
2312 }
2313
2314 QgsExpressionContext subContext( *context );
2315 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2316 subContext.setFeature( feature );
2317
2318 if ( isMaptip )
2319 {
2320 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2321 }
2322 else
2323 {
2324 QgsExpression exp( layer->displayExpression() );
2325 exp.prepare( &subContext );
2326 return exp.evaluate( &subContext ).toString();
2327 }
2328}
2329
2330static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2331{
2332 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2333}
2334
2335static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2336{
2337 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2338}
2339
2340static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2341{
2342 QgsFeature feature;
2343 QVariant layer;
2344 if ( values.isEmpty() )
2345 {
2346 feature = context->feature();
2347 layer = context->variable( QStringLiteral( "layer" ) );
2348 }
2349 else if ( values.size() == 1 )
2350 {
2351 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2352 layer = context->variable( QStringLiteral( "layer" ) );
2353 }
2354 else if ( values.size() == 2 )
2355 {
2356 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2357 layer = values.at( 0 );
2358 }
2359 else
2360 {
2361 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2362 return QVariant();
2363 }
2364
2365 bool foundLayer = false;
2366 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2367 {
2368 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2369 if ( !layer || !feature.isValid() )
2370 {
2371 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2372 }
2373
2374 return layer->selectedFeatureIds().contains( feature.id() );
2375 }, foundLayer );
2376 if ( !foundLayer )
2377 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2378 else
2379 return res;
2380}
2381
2382static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2383{
2384 QVariant layer;
2385
2386 if ( values.isEmpty() )
2387 layer = context->variable( QStringLiteral( "layer" ) );
2388 else if ( values.count() == 1 )
2389 layer = values.at( 0 );
2390 else
2391 {
2392 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2393 return QVariant();
2394 }
2395
2396 bool foundLayer = false;
2397 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2398 {
2399 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2400 if ( !layer )
2401 {
2402 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2403 }
2404
2405 return layer->selectedFeatureCount();
2406 }, foundLayer );
2407 if ( !foundLayer )
2408 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2409 else
2410 return res;
2411}
2412
2413static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2414{
2415 static QMap<QString, qlonglong> counterCache;
2416 QVariant functionResult;
2417
2418 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2419 {
2420 QString database;
2421
2422 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2423
2424 if ( layer )
2425 {
2426 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2427 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2428 if ( database.isEmpty() )
2429 {
2430 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2431 }
2432 }
2433 else
2434 {
2435 database = databaseArgument;
2436 }
2437
2438 const QString table = values.at( 1 ).toString();
2439 const QString idColumn = values.at( 2 ).toString();
2440 const QString filterAttribute = values.at( 3 ).toString();
2441 const QVariant filterValue = values.at( 4 ).toString();
2442 const QVariantMap defaultValues = values.at( 5 ).toMap();
2443
2444 // read from database
2446 sqlite3_statement_unique_ptr sqliteStatement;
2447
2448 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2449 {
2450 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2451 functionResult = QVariant();
2452 return;
2453 }
2454
2455 QString errorMessage;
2456 QString currentValSql;
2457
2458 qlonglong nextId = 0;
2459 bool cachedMode = false;
2460 bool valueRetrieved = false;
2461
2462 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2463
2464 // Running in transaction mode, check for cached value first
2465 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2466 {
2467 cachedMode = true;
2468
2469 auto cachedCounter = counterCache.find( cacheString );
2470
2471 if ( cachedCounter != counterCache.end() )
2472 {
2473 qlonglong &cachedValue = cachedCounter.value();
2474 nextId = cachedValue;
2475 nextId += 1;
2476 cachedValue = nextId;
2477 valueRetrieved = true;
2478 }
2479 }
2480
2481 // Either not in cached mode or no cached value found, obtain from DB
2482 if ( !cachedMode || !valueRetrieved )
2483 {
2484 int result = SQLITE_ERROR;
2485
2486 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2487 if ( !filterAttribute.isNull() )
2488 {
2489 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2490 }
2491
2492 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2493
2494 if ( result == SQLITE_OK )
2495 {
2496 nextId = 0;
2497 if ( sqliteStatement.step() == SQLITE_ROW )
2498 {
2499 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2500 }
2501
2502 // If in cached mode: add value to cache and connect to transaction
2503 if ( cachedMode && result == SQLITE_OK )
2504 {
2505 counterCache.insert( cacheString, nextId );
2506
2507 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2508 {
2509 counterCache.remove( cacheString );
2510 } );
2511 }
2512 valueRetrieved = true;
2513 }
2514 }
2515
2516 if ( valueRetrieved )
2517 {
2518 QString upsertSql;
2519 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2520 QStringList cols;
2521 QStringList vals;
2522 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2523 vals << QgsSqliteUtils::quotedValue( nextId );
2524
2525 if ( !filterAttribute.isNull() )
2526 {
2527 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2528 vals << QgsSqliteUtils::quotedValue( filterValue );
2529 }
2530
2531 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2532 {
2533 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2534 vals << iter.value().toString();
2535 }
2536
2537 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2538 upsertSql += QLatin1String( " VALUES " );
2539 upsertSql += '(' + vals.join( ',' ) + ')';
2540
2541 int result = SQLITE_ERROR;
2542 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2543 {
2544 QgsTransaction *transaction = layer->dataProvider()->transaction();
2545 if ( transaction->executeSql( upsertSql, errorMessage ) )
2546 {
2547 result = SQLITE_OK;
2548 }
2549 }
2550 else
2551 {
2552 result = sqliteDb.exec( upsertSql, errorMessage );
2553 }
2554 if ( result == SQLITE_OK )
2555 {
2556 functionResult = QVariant( nextId );
2557 return;
2558 }
2559 else
2560 {
2561 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2562 functionResult = QVariant();
2563 return;
2564 }
2565 }
2566
2567 functionResult = QVariant();
2568 };
2569
2570 bool foundLayer = false;
2571 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2572 {
2573 fetchAndIncrementFunc( layer, QString() );
2574 }, foundLayer );
2575 if ( !foundLayer )
2576 {
2577 const QString databasePath = values.at( 0 ).toString();
2578 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2579 {
2580 fetchAndIncrementFunc( nullptr, databasePath );
2581 } );
2582 }
2583
2584 return functionResult;
2585}
2586
2587static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2588{
2589 QString concat;
2590 for ( const QVariant &value : values )
2591 {
2592 if ( !QgsVariantUtils::isNull( value ) )
2593 concat += QgsExpressionUtils::getStringValue( value, parent );
2594 }
2595 return concat;
2596}
2597
2598static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2599{
2600 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2601 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2602}
2603
2604static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2605{
2606 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2607 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2608 return string.right( pos );
2609}
2610
2611static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2612{
2613 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2614 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2615 return string.left( pos );
2616}
2617
2618static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2619{
2620 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2621 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2622 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2623 return string.leftJustified( length, fill.at( 0 ), true );
2624}
2625
2626static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2627{
2628 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2629 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2630 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2631 return string.rightJustified( length, fill.at( 0 ), true );
2632}
2633
2634static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2635{
2636 if ( values.size() < 1 )
2637 {
2638 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2639 return QVariant();
2640 }
2641
2642 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2643 for ( int n = 1; n < values.length(); n++ )
2644 {
2645 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2646 }
2647 return string;
2648}
2649
2650
2651static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2652{
2653 return QVariant( QDateTime::currentDateTime() );
2654}
2655
2656static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2657{
2658 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2659 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2660 if ( format.isEmpty() && !language.isEmpty() )
2661 {
2662 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2663 return QVariant( QDate() );
2664 }
2665
2666 if ( format.isEmpty() && language.isEmpty() )
2667 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2668
2669 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2670 QLocale locale = QLocale();
2671 if ( !language.isEmpty() )
2672 {
2673 locale = QLocale( language );
2674 }
2675
2676 QDate date = locale.toDate( datestring, format );
2677 if ( !date.isValid() )
2678 {
2679 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2680 date = QDate();
2681 }
2682 return QVariant( date );
2683}
2684
2685static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2686{
2687 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2688 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2689 if ( format.isEmpty() && !language.isEmpty() )
2690 {
2691 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2692 return QVariant( QTime() );
2693 }
2694
2695 if ( format.isEmpty() && language.isEmpty() )
2696 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2697
2698 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2699 QLocale locale = QLocale();
2700 if ( !language.isEmpty() )
2701 {
2702 locale = QLocale( language );
2703 }
2704
2705 QTime time = locale.toTime( timestring, format );
2706 if ( !time.isValid() )
2707 {
2708 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2709 time = QTime();
2710 }
2711 return QVariant( time );
2712}
2713
2714static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2715{
2716 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2717}
2718
2719/*
2720 * DMS functions
2721 */
2722
2723static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2724{
2725 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2726 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2727 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2728
2729 QString formatString;
2730 if ( values.count() > 3 )
2731 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2732
2734 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2735 {
2737 }
2738 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2739 {
2741 }
2742 else if ( ! formatString.isEmpty() )
2743 {
2744 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2745 return QVariant();
2746 }
2747
2748 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2749 {
2750 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2751 }
2752 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2753 {
2754 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2755 }
2756 else
2757 {
2758 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2759 return QVariant();
2760 }
2761}
2762
2763static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2764{
2766 return floatToDegreeFormat( format, values, context, parent, node );
2767}
2768
2769static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2770{
2771 double value = 0.0;
2772 bool ok = false;
2773 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2774
2775 return ok ? QVariant( value ) : QVariant();
2776}
2777
2778static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2779{
2781 return floatToDegreeFormat( format, values, context, parent, node );
2782}
2783
2784static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2785{
2786 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2787 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2788 qint64 seconds = d2.secsTo( d1 );
2789 return QVariant::fromValue( QgsInterval( seconds ) );
2790}
2791
2792static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2793{
2794 if ( !values.at( 0 ).canConvert<QDate>() )
2795 return QVariant();
2796
2797 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2798 if ( !date.isValid() )
2799 return QVariant();
2800
2801 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2802 // (to match PostgreSQL behavior)
2803 return date.dayOfWeek() % 7;
2804}
2805
2806static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2807{
2808 QVariant value = values.at( 0 );
2809 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2810 if ( inter.isValid() )
2811 {
2812 return QVariant( inter.days() );
2813 }
2814 else
2815 {
2816 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2817 return QVariant( d1.date().day() );
2818 }
2819}
2820
2821static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2822{
2823 QVariant value = values.at( 0 );
2824 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2825 if ( inter.isValid() )
2826 {
2827 return QVariant( inter.years() );
2828 }
2829 else
2830 {
2831 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2832 return QVariant( d1.date().year() );
2833 }
2834}
2835
2836static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2837{
2838 QVariant value = values.at( 0 );
2839 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2840 if ( inter.isValid() )
2841 {
2842 return QVariant( inter.months() );
2843 }
2844 else
2845 {
2846 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2847 return QVariant( d1.date().month() );
2848 }
2849}
2850
2851static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2852{
2853 QVariant value = values.at( 0 );
2854 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2855 if ( inter.isValid() )
2856 {
2857 return QVariant( inter.weeks() );
2858 }
2859 else
2860 {
2861 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2862 return QVariant( d1.date().weekNumber() );
2863 }
2864}
2865
2866static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2867{
2868 QVariant value = values.at( 0 );
2869 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2870 if ( inter.isValid() )
2871 {
2872 return QVariant( inter.hours() );
2873 }
2874 else
2875 {
2876 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2877 return QVariant( t1.hour() );
2878 }
2879}
2880
2881static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2882{
2883 QVariant value = values.at( 0 );
2884 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2885 if ( inter.isValid() )
2886 {
2887 return QVariant( inter.minutes() );
2888 }
2889 else
2890 {
2891 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2892 return QVariant( t1.minute() );
2893 }
2894}
2895
2896static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2897{
2898 QVariant value = values.at( 0 );
2899 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2900 if ( inter.isValid() )
2901 {
2902 return QVariant( inter.seconds() );
2903 }
2904 else
2905 {
2906 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
2907 return QVariant( t1.second() );
2908 }
2909}
2910
2911static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2912{
2913 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2914 if ( dt.isValid() )
2915 {
2916 return QVariant( dt.toMSecsSinceEpoch() );
2917 }
2918 else
2919 {
2920 return QVariant();
2921 }
2922}
2923
2924static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2925{
2926 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
2927 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
2928 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
2929}
2930
2931static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2932{
2933 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2934 if ( parent->hasEvalError() )
2935 {
2936 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
2937 return QVariant();
2938 }
2939 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2940 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
2941}
2942
2943static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2945 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
2946 if ( parent->hasEvalError() )
2947 {
2948 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
2949 return QVariant();
2950 }
2951 bool ok;
2952 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
2953}
2954
2955#define ENSURE_GEOM_TYPE(f, g, geomtype) \
2956 if ( !(f).hasGeometry() ) \
2957 return QVariant(); \
2958 QgsGeometry g = (f).geometry(); \
2959 if ( (g).type() != (geomtype) ) \
2960 return QVariant();
2961
2962static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2963{
2964 FEAT_FROM_CONTEXT( context, f )
2966 if ( g.isMultipart() )
2967 {
2968 return g.asMultiPoint().at( 0 ).x();
2969 }
2970 else
2971 {
2972 return g.asPoint().x();
2973 }
2974}
2975
2976static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2977{
2978 FEAT_FROM_CONTEXT( context, f )
2980 if ( g.isMultipart() )
2981 {
2982 return g.asMultiPoint().at( 0 ).y();
2983 }
2984 else
2985 {
2986 return g.asPoint().y();
2987 }
2988}
2989
2990static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
2991{
2992 FEAT_FROM_CONTEXT( context, f )
2994
2995 if ( g.isEmpty() )
2996 return QVariant();
2997
2998 const QgsAbstractGeometry *abGeom = g.constGet();
2999
3000 if ( g.isEmpty() || !abGeom->is3D() )
3001 return QVariant();
3002
3003 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3004 {
3005 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3006 if ( point )
3007 return point->z();
3008 }
3009 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3010 {
3011 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3012 {
3013 if ( collection->numGeometries() > 0 )
3014 {
3015 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3016 return point->z();
3017 }
3018 }
3019 }
3020
3021 return QVariant();
3022}
3023
3024static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3025{
3026 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3027 if ( geom.isNull() )
3028 return QVariant();
3029
3030 bool isValid = geom.isGeosValid();
3031
3032 return QVariant( isValid );
3033}
3034
3035static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3036{
3037 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3038 if ( geom.isNull() )
3039 return QVariant();
3040
3041 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3042#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3044#else
3046#endif
3047 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3049 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3051
3052 const bool keepCollapsed = values.value( 2 ).toBool();
3053
3054 QgsGeometry valid;
3055 try
3056 {
3057 valid = geom.makeValid( method, keepCollapsed );
3058 }
3059 catch ( QgsNotSupportedException & )
3060 {
3061 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3062 return QVariant();
3063 }
3064
3065 return QVariant::fromValue( valid );
3066}
3067
3068static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3069{
3070 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3071 if ( geom.isNull() )
3072 return QVariant();
3073
3074 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3075 QVariantList array;
3076 for ( int i = 0; i < multiGeom.size(); ++i )
3077 {
3078 array += QVariant::fromValue( multiGeom.at( i ) );
3079 }
3080
3081 return array;
3082}
3083
3084static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3085{
3086 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3087 if ( geom.isNull() )
3088 return QVariant();
3089
3090 //if single point, return the point's x coordinate
3091 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3092 {
3093 return geom.asPoint().x();
3094 }
3095
3096 //otherwise return centroid x
3097 QgsGeometry centroid = geom.centroid();
3098 QVariant result( centroid.asPoint().x() );
3099 return result;
3100}
3101
3102static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3103{
3104 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3105 if ( geom.isNull() )
3106 return QVariant();
3107
3108 //if single point, return the point's y coordinate
3109 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3110 {
3111 return geom.asPoint().y();
3112 }
3113
3114 //otherwise return centroid y
3115 QgsGeometry centroid = geom.centroid();
3116 QVariant result( centroid.asPoint().y() );
3117 return result;
3118}
3119
3120static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3121{
3122 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3123 if ( geom.isNull() )
3124 return QVariant(); //or 0?
3125
3126 if ( !geom.constGet()->is3D() )
3127 return QVariant();
3128
3129 //if single point, return the point's z coordinate
3130 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3131 {
3132 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3133 if ( point )
3134 return point->z();
3135 }
3136 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3137 {
3138 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3139 {
3140 if ( collection->numGeometries() == 1 )
3141 {
3142 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3143 return point->z();
3144 }
3145 }
3146 }
3147
3148 return QVariant();
3149}
3150
3151static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3152{
3153 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3154 if ( geom.isNull() )
3155 return QVariant(); //or 0?
3156
3157 if ( !geom.constGet()->isMeasure() )
3158 return QVariant();
3159
3160 //if single point, return the point's m value
3161 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3162 {
3163 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3164 if ( point )
3165 return point->m();
3166 }
3167 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3168 {
3169 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3170 {
3171 if ( collection->numGeometries() == 1 )
3172 {
3173 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3174 return point->m();
3175 }
3176 }
3177 }
3178
3179 return QVariant();
3180}
3181
3182static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3183{
3184 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3185
3186 if ( geom.isNull() )
3187 return QVariant();
3188
3189 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3190
3191 if ( idx < 0 )
3192 {
3193 //negative idx
3194 int count = geom.constGet()->nCoordinates();
3195 idx = count + idx;
3196 }
3197 else
3198 {
3199 //positive idx is 1 based
3200 idx -= 1;
3201 }
3202
3203 QgsVertexId vId;
3204 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3205 {
3206 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3207 return QVariant();
3208 }
3209
3210 QgsPoint point = geom.constGet()->vertexAt( vId );
3211 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3212}
3213
3214static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3215{
3216 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3217
3218 if ( geom.isNull() )
3219 return QVariant();
3220
3221 QgsVertexId vId;
3222 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3223 {
3224 return QVariant();
3225 }
3226
3227 QgsPoint point = geom.constGet()->vertexAt( vId );
3228 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3229}
3230
3231static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3232{
3233 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3234
3235 if ( geom.isNull() )
3236 return QVariant();
3237
3238 QgsVertexId vId;
3239 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3240 {
3241 return QVariant();
3242 }
3243
3244 QgsPoint point = geom.constGet()->vertexAt( vId );
3245 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3246}
3247
3248static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3249{
3250 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3251
3252 if ( geom.isNull() )
3253 return QVariant();
3254
3255 bool ignoreClosing = false;
3256 if ( values.length() > 1 )
3257 {
3258 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3259 }
3260
3261 QgsMultiPoint *mp = new QgsMultiPoint();
3262
3263 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3264 for ( const QgsRingSequence &part : sequence )
3265 {
3266 for ( const QgsPointSequence &ring : part )
3267 {
3268 bool skipLast = false;
3269 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3270 {
3271 skipLast = true;
3272 }
3273
3274 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3275 {
3276 mp->addGeometry( ring.at( i ).clone() );
3277 }
3278 }
3279 }
3280
3281 return QVariant::fromValue( QgsGeometry( mp ) );
3282}
3283
3284static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3285{
3286 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3287
3288 if ( geom.isNull() )
3289 return QVariant();
3290
3291 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3292
3293 //OK, now we have a complete list of segmentized lines from the geometry
3295 for ( QgsLineString *line : linesToProcess )
3296 {
3297 for ( int i = 0; i < line->numPoints() - 1; ++i )
3298 {
3300 segment->setPoints( QgsPointSequence()
3301 << line->pointN( i )
3302 << line->pointN( i + 1 ) );
3303 ml->addGeometry( segment );
3304 }
3305 delete line;
3306 }
3307
3308 return QVariant::fromValue( QgsGeometry( ml ) );
3309}
3310
3311static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3312{
3313 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3314
3315 if ( geom.isNull() )
3316 return QVariant();
3317
3318 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
3319 if ( !curvePolygon && geom.isMultipart() )
3320 {
3321 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3322 {
3323 if ( collection->numGeometries() == 1 )
3324 {
3325 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3326 }
3327 }
3328 }
3329
3330 if ( !curvePolygon )
3331 return QVariant();
3332
3333 //idx is 1 based
3334 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3335
3336 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3337 return QVariant();
3338
3339 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3340 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3341 return result;
3342}
3343
3344static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3345{
3346 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3347
3348 if ( geom.isNull() )
3349 return QVariant();
3350
3351 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
3352 if ( !collection )
3353 return QVariant();
3354
3355 //idx is 1 based
3356 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3357
3358 if ( idx < 0 || idx >= collection->numGeometries() )
3359 return QVariant();
3360
3361 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3362 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3363 return result;
3364}
3365
3366static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3367{
3368 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3369
3370 if ( geom.isNull() )
3371 return QVariant();
3372
3373 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3374 if ( !boundary )
3375 return QVariant();
3376
3377 return QVariant::fromValue( QgsGeometry( boundary ) );
3378}
3379
3380static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3381{
3382 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3383
3384 if ( geom.isNull() )
3385 return QVariant();
3386
3387 QgsGeometry merged = geom.mergeLines();
3388 if ( merged.isNull() )
3389 return QVariant();
3390
3391 return QVariant::fromValue( merged );
3392}
3393
3394static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3395{
3396 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3397 if ( geom.isNull() )
3398 return QVariant();
3399
3400 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3401 if ( geom2.isNull() )
3402 return QVariant();
3403
3404 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3405 if ( sharedPaths.isNull() )
3406 return QVariant();
3407
3408 return QVariant::fromValue( sharedPaths );
3409}
3410
3411
3412static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3413{
3414 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3415
3416 if ( geom.isNull() )
3417 return QVariant();
3418
3419 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3420
3421 QgsGeometry simplified = geom.simplify( tolerance );
3422 if ( simplified.isNull() )
3423 return QVariant();
3424
3425 return simplified;
3426}
3427
3428static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3429{
3430 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3431
3432 if ( geom.isNull() )
3433 return QVariant();
3434
3435 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3436
3438
3439 QgsGeometry simplified = simplifier.simplify( geom );
3440 if ( simplified.isNull() )
3441 return QVariant();
3442
3443 return simplified;
3444}
3445
3446static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3447{
3448 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3449
3450 if ( geom.isNull() )
3451 return QVariant();
3452
3453 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3454 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3455 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3456 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3457
3458 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3459 if ( smoothed.isNull() )
3460 return QVariant();
3461
3462 return smoothed;
3463}
3464
3465static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3466{
3467 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3468
3469 if ( geom.isNull() )
3470 return QVariant();
3471
3472 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3473 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3474 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3475
3476 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3477 if ( waved.isNull() )
3478 return QVariant();
3479
3480 return waved;
3481}
3482
3483static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3484{
3485 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3486
3487 if ( geom.isNull() )
3488 return QVariant();
3489
3490 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3491 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3492 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3493 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3494 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3495
3496 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3497 minAmplitude, maxAmplitude, seed );
3498 if ( waved.isNull() )
3499 return QVariant();
3500
3501 return waved;
3502}
3503
3504static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3505{
3506 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3507
3508 if ( geom.isNull() )
3509 return QVariant();
3510
3511 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3512 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3513 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3514
3515 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3516 if ( waved.isNull() )
3517 return QVariant();
3518
3519 return waved;
3520}
3521
3522static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3523{
3524 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3525
3526 if ( geom.isNull() )
3527 return QVariant();
3528
3529 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3530 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3531 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3532 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3533 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3534
3535 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3536 minAmplitude, maxAmplitude, seed );
3537 if ( waved.isNull() )
3538 return QVariant();
3539
3540 return waved;
3541}
3542
3543static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3544{
3545 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3546
3547 if ( geom.isNull() )
3548 return QVariant();
3549
3550 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3551 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3552 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3553
3554 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3555 if ( waved.isNull() )
3556 return QVariant();
3557
3558 return waved;
3559}
3560
3561static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3562{
3563 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3564
3565 if ( geom.isNull() )
3566 return QVariant();
3567
3568 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3569 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3570 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3571 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3572 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3573
3574 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3575 minAmplitude, maxAmplitude, seed );
3576 if ( waved.isNull() )
3577 return QVariant();
3578
3579 return waved;
3580}
3581
3582static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3583{
3584 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3585
3586 if ( geom.isNull() )
3587 return QVariant();
3588
3589 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3590 QVector< double > dashPattern;
3591 dashPattern.reserve( pattern.size() );
3592 for ( const QVariant &value : std::as_const( pattern ) )
3593 {
3594 bool ok = false;
3595 double v = value.toDouble( &ok );
3596 if ( ok )
3597 {
3598 dashPattern << v;
3599 }
3600 else
3601 {
3602 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3603 return QgsGeometry();
3604 }
3605 }
3606
3607 if ( dashPattern.size() % 2 != 0 )
3608 {
3609 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3610 return QgsGeometry();
3611 }
3612
3613 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3615 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3617 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3619 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3621 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3623 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3625 else
3626 {
3627 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3628 return QgsGeometry();
3629 }
3630
3631 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3633 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3635 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3637 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3639 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3641 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3643 else
3644 {
3645 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3646 return QgsGeometry();
3647 }
3648
3649 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3651 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3653 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3655 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3657 else
3658 {
3659 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3660 return QgsGeometry();
3661 }
3662
3663 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3664
3665 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3666 if ( result.isNull() )
3667 return QVariant();
3668
3669 return result;
3670}
3671
3672static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3673{
3674 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3675
3676 if ( geom.isNull() )
3677 return QVariant();
3678
3679 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3680 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3681 if ( densified.isNull() )
3682 return QVariant();
3683
3684 return densified;
3685}
3686
3687static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3688{
3689 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3690
3691 if ( geom.isNull() )
3692 return QVariant();
3693
3694 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3695 const QgsGeometry densified = geom.densifyByDistance( distance );
3696 if ( densified.isNull() )
3697 return QVariant();
3698
3699 return densified;
3700}
3701
3702static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3703{
3704 QVariantList list;
3705 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3706 {
3707 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3708 }
3709 else
3710 {
3711 list = values;
3712 }
3713
3714 QVector< QgsGeometry > parts;
3715 parts.reserve( list.size() );
3716 for ( const QVariant &value : std::as_const( list ) )
3717 {
3718 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
3719 if ( part.isNull() )
3720 return QgsGeometry();
3721 parts << part;
3722 }
3723
3724 return QgsGeometry::collectGeometry( parts );
3725}
3726
3727static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3728{
3729 if ( values.count() < 2 || values.count() > 4 )
3730 {
3731 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3732 return QVariant();
3733 }
3734
3735 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3736 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3737 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3738 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3739 switch ( values.count() )
3740 {
3741 case 2:
3742 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3743 case 3:
3744 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3745 case 4:
3746 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3747 }
3748 return QVariant(); //avoid warning
3749}
3750
3751static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3752{
3753 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3754 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3755 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3756 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3757}
3758
3759static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3760{
3761 if ( values.empty() )
3762 {
3763 return QVariant();
3764 }
3765
3766 QVector<QgsPoint> points;
3767 points.reserve( values.count() );
3768
3769 auto addPoint = [&points]( const QgsGeometry & geom )
3770 {
3771 if ( geom.isNull() )
3772 return;
3773
3774 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3775 return;
3776
3777 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3778 if ( !point )
3779 return;
3780
3781 points << *point;
3782 };
3783
3784 for ( const QVariant &value : values )
3785 {
3786 if ( value.userType() == QMetaType::Type::QVariantList )
3787 {
3788 const QVariantList list = value.toList();
3789 for ( const QVariant &v : list )
3790 {
3791 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3792 }
3793 }
3794 else
3795 {
3796 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3797 }
3798 }
3799
3800 if ( points.count() < 2 )
3801 return QVariant();
3802
3803 return QgsGeometry( new QgsLineString( points ) );
3804}
3805
3806static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3807{
3808 if ( values.count() < 1 )
3809 {
3810 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3811 return QVariant();
3812 }
3813
3814 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3815
3816 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3817 return outerRing; // if it's already a polygon we have nothing to do
3818
3819 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3820 return QVariant();
3821
3822 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
3823
3824 const QgsCurve *exteriorRing = qgsgeometry_cast< QgsCurve * >( outerRing.constGet() );
3825 if ( !exteriorRing && outerRing.isMultipart() )
3826 {
3827 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( outerRing.constGet() ) )
3828 {
3829 if ( collection->numGeometries() == 1 )
3830 {
3831 exteriorRing = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3832 }
3833 }
3834 }
3835
3836 if ( !exteriorRing )
3837 return QVariant();
3838
3839 polygon->setExteriorRing( exteriorRing->segmentize() );
3840
3841
3842 for ( int i = 1; i < values.count(); ++i )
3843 {
3844 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
3845 if ( ringGeom.isNull() )
3846 continue;
3847
3848 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
3849 continue;
3850
3851 const QgsCurve *ring = qgsgeometry_cast< QgsCurve * >( ringGeom.constGet() );
3852 if ( !ring && ringGeom.isMultipart() )
3853 {
3854 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( ringGeom.constGet() ) )
3855 {
3856 if ( collection->numGeometries() == 1 )
3857 {
3858 ring = qgsgeometry_cast< QgsCurve * >( collection->geometryN( 0 ) );
3859 }
3860 }
3861 }
3862
3863 if ( !ring )
3864 continue;
3865
3866 polygon->addInteriorRing( ring->segmentize() );
3867 }
3868
3869 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
3870}
3871
3872static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3873{
3874 std::unique_ptr<QgsTriangle> tr( new QgsTriangle() );
3875 std::unique_ptr<QgsLineString> lineString( new QgsLineString() );
3876 lineString->clear();
3877
3878 for ( const QVariant &value : values )
3879 {
3880 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
3881 if ( geom.isNull() )
3882 return QVariant();
3883
3884 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3885 return QVariant();
3886
3887 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3888 if ( !point && geom.isMultipart() )
3889 {
3890 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3891 {
3892 if ( collection->numGeometries() == 1 )
3893 {
3894 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3895 }
3896 }
3897 }
3898
3899 if ( !point )
3900 return QVariant();
3901
3902 lineString->addVertex( *point );
3903 }
3904
3905 tr->setExteriorRing( lineString.release() );
3906
3907 return QVariant::fromValue( QgsGeometry( tr.release() ) );
3908}
3909
3910static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3911{
3912 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3913 if ( geom.isNull() )
3914 return QVariant();
3915
3916 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3917 return QVariant();
3918
3919 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3920 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
3921
3922 if ( segment < 3 )
3923 {
3924 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3925 return QVariant();
3926 }
3927 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3928 if ( !point && geom.isMultipart() )
3929 {
3930 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3931 {
3932 if ( collection->numGeometries() == 1 )
3933 {
3934 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3935 }
3936 }
3937 }
3938 if ( !point )
3939 return QVariant();
3940
3941 QgsCircle circ( *point, radius );
3942 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3943}
3944
3945static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3946{
3947 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3948 if ( geom.isNull() )
3949 return QVariant();
3950
3951 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3952 return QVariant();
3953
3954 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3955 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3956 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3957 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
3958 if ( segment < 3 )
3959 {
3960 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
3961 return QVariant();
3962 }
3963 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( geom.constGet() );
3964 if ( !point && geom.isMultipart() )
3965 {
3966 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() ) )
3967 {
3968 if ( collection->numGeometries() == 1 )
3969 {
3970 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
3971 }
3972 }
3973 }
3974 if ( !point )
3975 return QVariant();
3976
3977 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
3978 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
3979}
3980
3981static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3982{
3983
3984 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3985 if ( pt1.isNull() )
3986 return QVariant();
3987
3988 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
3989 return QVariant();
3990
3991 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3992 if ( pt2.isNull() )
3993 return QVariant();
3994
3995 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
3996 return QVariant();
3997
3998 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
3999 if ( nbEdges < 3 )
4000 {
4001 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4002 return QVariant();
4003 }
4004
4005 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4007 {
4008 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4009 return QVariant();
4010 }
4011
4012 const QgsPoint *center = qgsgeometry_cast< const QgsPoint * >( pt1.constGet() );
4013 if ( !center && pt1.isMultipart() )
4014 {
4015 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt1.constGet() ) )
4016 {
4017 if ( collection->numGeometries() == 1 )
4018 {
4019 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4020 }
4021 }
4022 }
4023 if ( !center )
4024 return QVariant();
4025
4026 const QgsPoint *corner = qgsgeometry_cast< const QgsPoint * >( pt2.constGet() );
4027 if ( !corner && pt2.isMultipart() )
4028 {
4029 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( pt2.constGet() ) )
4030 {
4031 if ( collection->numGeometries() == 1 )
4032 {
4033 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4034 }
4035 }
4036 }
4037 if ( !corner )
4038 return QVariant();
4039
4040 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4041
4042 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4043
4044}
4045
4046static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4047{
4048 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4049 if ( pt1.isNull() )
4050 return QVariant();
4051 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4052 return QVariant();
4053
4054 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4055 if ( pt2.isNull() )
4056 return QVariant();
4057 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4058 return QVariant();
4059
4060 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4061 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4062 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4063
4064 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4065}
4066
4067static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4068{
4069 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4070 if ( pt1.isNull() )
4071 return QVariant();
4072 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4073 return QVariant();
4074
4075 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4076 if ( pt2.isNull() )
4077 return QVariant();
4078 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4079 return QVariant();
4080
4081 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4082 if ( pt3.isNull() )
4083 return QVariant();
4084 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4085 return QVariant();
4086
4087 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4088 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4089 {
4090 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4091 return QVariant();
4092 }
4093 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4094 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4095 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4096 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4097 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4098}
4099
4100static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4101{
4102 if ( geom.isNull() )
4103 return QVariant();
4104
4105 if ( idx < 0 )
4106 {
4107 idx += geom.constGet()->nCoordinates();
4108 }
4109 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4110 {
4111 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4112 return QVariant();
4113 }
4114 return QVariant::fromValue( geom.vertexAt( idx ) );
4115}
4116
4117// function used for the old $ style
4118static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4119{
4120 FEAT_FROM_CONTEXT( context, feature )
4121 const QgsGeometry geom = feature.geometry();
4122 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4123
4124 const QVariant v = pointAt( geom, idx, parent );
4125
4126 if ( !v.isNull() )
4127 return QVariant( v.value<QgsPoint>().x() );
4128 else
4129 return QVariant();
4130}
4131static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4132{
4133 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias x_at function is called like a $ function (x_at(i))
4134 {
4135 return fcnOldXat( values, f, parent, node );
4136 }
4137 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4138 {
4139 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4140 }
4141
4142 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4143 if ( geom.isNull() )
4144 {
4145 return QVariant();
4146 }
4147
4148 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4149
4150 const QVariant v = pointAt( geom, vertexNumber, parent );
4151 if ( !v.isNull() )
4152 return QVariant( v.value<QgsPoint>().x() );
4153 else
4154 return QVariant();
4155}
4156
4157// function used for the old $ style
4158static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4159{
4160 FEAT_FROM_CONTEXT( context, feature )
4161 const QgsGeometry geom = feature.geometry();
4162 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4163
4164 const QVariant v = pointAt( geom, idx, parent );
4165
4166 if ( !v.isNull() )
4167 return QVariant( v.value<QgsPoint>().y() );
4168 else
4169 return QVariant();
4170}
4171static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4172{
4173 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias y_at function is called like a $ function (y_at(i))
4174 {
4175 return fcnOldYat( values, f, parent, node );
4176 }
4177 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4178 {
4179 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4180 }
4181
4182 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4183 if ( geom.isNull() )
4184 {
4185 return QVariant();
4186 }
4187
4188 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4189
4190 const QVariant v = pointAt( geom, vertexNumber, parent );
4191 if ( !v.isNull() )
4192 return QVariant( v.value<QgsPoint>().y() );
4193 else
4194 return QVariant();
4195}
4196
4197static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4198{
4199 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4200 if ( geom.isNull() )
4201 {
4202 return QVariant();
4203 }
4204
4205 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4206
4207 const QVariant v = pointAt( geom, vertexNumber, parent );
4208 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4209 return QVariant( v.value<QgsPoint>().z() );
4210 else
4211 return QVariant();
4212}
4213
4214static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4215{
4216 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4217 if ( geom.isNull() )
4218 {
4219 return QVariant();
4220 }
4221
4222 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4223
4224 const QVariant v = pointAt( geom, vertexNumber, parent );
4225 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4226 return QVariant( v.value<QgsPoint>().m() );
4227 else
4228 return QVariant();
4229}
4230
4231
4232static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4233{
4234 if ( !context )
4235 return QVariant();
4236
4237 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4238 if ( context->hasGeometry() )
4239 return context->geometry();
4240 else
4241 {
4242 FEAT_FROM_CONTEXT( context, f )
4243 QgsGeometry geom = f.geometry();
4244 if ( !geom.isNull() )
4245 return QVariant::fromValue( geom );
4246 else
4247 return QVariant();
4248 }
4249}
4250
4251static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4252{
4253 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4254 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4255 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4256 return result;
4257}
4258
4259static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4260{
4261 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4262 if ( wkb.isNull() )
4263 return QVariant();
4264
4265 QgsGeometry geom;
4266 geom.fromWkb( wkb );
4267 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4268}
4269
4270static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4271{
4272 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4273 QgsOgcUtils::Context ogcContext;
4274 if ( context )
4275 {
4276 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4277 if ( mapLayerPtr )
4278 {
4279 ogcContext.layer = mapLayerPtr.data();
4280 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4281 }
4282 }
4283 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4284 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4285 return result;
4286}
4287
4288static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4289{
4290 FEAT_FROM_CONTEXT( context, f )
4292 QgsDistanceArea *calc = parent->geomCalculator();
4293 if ( calc )
4294 {
4295 try
4296 {
4297 double area = calc->measureArea( f.geometry() );
4298 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4299 return QVariant( area );
4300 }
4301 catch ( QgsCsException & )
4302 {
4303 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4304 return QVariant();
4305 }
4306 }
4307 else
4308 {
4309 return QVariant( f.geometry().area() );
4310 }
4311}
4312
4313static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4314{
4315 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4316
4317 if ( geom.type() != Qgis::GeometryType::Polygon )
4318 return QVariant();
4319
4320 return QVariant( geom.area() );
4321}
4322
4323static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4324{
4325 FEAT_FROM_CONTEXT( context, f )
4327 QgsDistanceArea *calc = parent->geomCalculator();
4328 if ( calc )
4329 {
4330 try
4331 {
4332 double len = calc->measureLength( f.geometry() );
4333 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4334 return QVariant( len );
4335 }
4336 catch ( QgsCsException & )
4337 {
4338 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4339 return QVariant();
4340 }
4341 }
4342 else
4343 {
4344 return QVariant( f.geometry().length() );
4345 }
4346}
4347
4348static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4349{
4350 FEAT_FROM_CONTEXT( context, f )
4352 QgsDistanceArea *calc = parent->geomCalculator();
4353 if ( calc )
4354 {
4355 try
4356 {
4357 double len = calc->measurePerimeter( f.geometry() );
4358 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4359 return QVariant( len );
4360 }
4361 catch ( QgsCsException & )
4362 {
4363 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4364 return QVariant();
4365 }
4366 }
4367 else
4368 {
4369 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4370 }
4371}
4372
4373static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4374{
4375 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4376
4377 if ( geom.type() != Qgis::GeometryType::Polygon )
4378 return QVariant();
4379
4380 //length for polygons = perimeter
4381 return QVariant( geom.length() );
4382}
4383
4384static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4385{
4386 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4387 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4388}
4389
4390static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4391{
4392 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4393 if ( geom.isNull() )
4394 return QVariant();
4395
4396 return QVariant( geom.constGet()->partCount() );
4397}
4398
4399static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4400{
4401 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4402 if ( geom.isNull() )
4403 return QVariant();
4404
4405 return QVariant( geom.isMultipart() );
4406}
4407
4408static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4409{
4410 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4411
4412 if ( geom.isNull() )
4413 return QVariant();
4414
4415 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4416 if ( curvePolygon )
4417 return QVariant( curvePolygon->numInteriorRings() );
4418
4419 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4420 if ( collection )
4421 {
4422 //find first CurvePolygon in collection
4423 for ( int i = 0; i < collection->numGeometries(); ++i )
4424 {
4425 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4426 if ( !curvePolygon )
4427 continue;
4428
4429 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4430 }
4431 }
4432
4433 return QVariant();
4434}
4435
4436static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4437{
4438 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4439
4440 if ( geom.isNull() )
4441 return QVariant();
4442
4443 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet() );
4444 if ( curvePolygon )
4445 return QVariant( curvePolygon->ringCount() );
4446
4447 bool foundPoly = false;
4448 int ringCount = 0;
4449 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() );
4450 if ( collection )
4451 {
4452 //find CurvePolygons in collection
4453 for ( int i = 0; i < collection->numGeometries(); ++i )
4454 {
4455 curvePolygon = qgsgeometry_cast< QgsCurvePolygon *>( collection->geometryN( i ) );
4456 if ( !curvePolygon )
4457 continue;
4458
4459 foundPoly = true;
4460 ringCount += curvePolygon->ringCount();
4461 }
4462 }
4463
4464 if ( !foundPoly )
4465 return QVariant();
4466
4467 return QVariant( ringCount );
4468}
4469
4470static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4471{
4472 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4473 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4474 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4475 return result;
4476}
4477
4478static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4479{
4480 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4481 return QVariant::fromValue( geom.boundingBox().width() );
4482}
4483
4484static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4485{
4486 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4487 return QVariant::fromValue( geom.boundingBox().height() );
4488}
4489
4490static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4491{
4492 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4493 if ( geom.isNull() )
4494 return QVariant();
4495
4497}
4498
4499static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4500{
4501 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4502 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4503}
4504
4505static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4506{
4507 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4508 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4509}
4510
4511static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4512{
4513 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4514 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4515}
4516
4517static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4518{
4519 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4520 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4521}
4522
4523static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4524{
4525 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4526
4527 if ( geom.isNull() || geom.isEmpty( ) )
4528 return QVariant();
4529
4530 if ( !geom.constGet()->is3D() )
4531 return QVariant();
4532
4533 double max = std::numeric_limits< double >::lowest();
4534
4535 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4536 {
4537 double z = ( *it ).z();
4538
4539 if ( max < z )
4540 max = z;
4541 }
4542
4543 if ( max == std::numeric_limits< double >::lowest() )
4544 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4545
4546 return QVariant( max );
4547}
4548
4549static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4550{
4551 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4552
4553 if ( geom.isNull() || geom.isEmpty() )
4554 return QVariant();
4555
4556 if ( !geom.constGet()->is3D() )
4557 return QVariant();
4558
4559 double min = std::numeric_limits< double >::max();
4560
4561 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4562 {
4563 double z = ( *it ).z();
4564
4565 if ( z < min )
4566 min = z;
4567 }
4568
4569 if ( min == std::numeric_limits< double >::max() )
4570 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4571
4572 return QVariant( min );
4573}
4574
4575static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4576{
4577 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4578
4579 if ( geom.isNull() || geom.isEmpty() )
4580 return QVariant();
4581
4582 if ( !geom.constGet()->isMeasure() )
4583 return QVariant();
4584
4585 double min = std::numeric_limits< double >::max();
4586
4587 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4588 {
4589 double m = ( *it ).m();
4590
4591 if ( m < min )
4592 min = m;
4593 }
4594
4595 if ( min == std::numeric_limits< double >::max() )
4596 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4597
4598 return QVariant( min );
4599}
4600
4601static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4602{
4603 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4604
4605 if ( geom.isNull() || geom.isEmpty() )
4606 return QVariant();
4607
4608 if ( !geom.constGet()->isMeasure() )
4609 return QVariant();
4610
4611 double max = std::numeric_limits< double >::lowest();
4612
4613 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4614 {
4615 double m = ( *it ).m();
4616
4617 if ( max < m )
4618 max = m;
4619 }
4620
4621 if ( max == std::numeric_limits< double >::lowest() )
4622 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4623
4624 return QVariant( max );
4625}
4626
4627static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4628{
4629 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4630 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom.constGet() );
4631 if ( !curve )
4632 {
4633 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4634 return QVariant();
4635 }
4636
4637 return QVariant( curve->sinuosity() );
4638}
4639
4640static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4641{
4642 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4643 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4644 if ( !curve )
4645 {
4646 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4647 return QVariant();
4648 }
4649
4650 return QVariant( curve->straightDistance2d() );
4651}
4652
4653static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4654{
4655 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4656 const QgsCurvePolygon *poly = geom.constGet() ? qgsgeometry_cast< const QgsCurvePolygon * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4657
4658 if ( !poly )
4659 {
4660 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4661 return QVariant();
4662 }
4663
4664 return QVariant( poly->roundness() );
4665}
4666
4667
4668
4669static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4670{
4671 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4672 if ( geom.isNull() )
4673 return QVariant();
4674
4675 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4676 flipped->swapXy();
4677 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4678}
4679
4680static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4681{
4682 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4683 if ( fGeom.isNull() )
4684 return QVariant();
4685
4686 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4687 if ( !curve && fGeom.isMultipart() )
4688 {
4689 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4690 {
4691 if ( collection->numGeometries() == 1 )
4692 {
4693 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4694 }
4695 }
4696 }
4697
4698 if ( !curve )
4699 return QVariant();
4700
4701 return QVariant::fromValue( curve->isClosed() );
4702}
4703
4704static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4705{
4706 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4707
4708 if ( geom.isNull() )
4709 return QVariant();
4710
4711 QVariant result;
4712 if ( !geom.isMultipart() )
4713 {
4714 const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( geom.constGet() );
4715
4716 if ( !line )
4717 return QVariant();
4718
4719 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4720 closedLine->close();
4721
4722 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4723 }
4724 else
4725 {
4726 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( geom.constGet() );
4727
4728 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4729
4730 for ( int i = 0; i < collection->numGeometries(); ++i )
4731 {
4732 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4733 {
4734 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4735 closedLine->close();
4736
4737 closed->addGeometry( closedLine.release() );
4738 }
4739 }
4740 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4741 }
4742
4743 return result;
4744}
4745
4746static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4747{
4748 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4749 if ( fGeom.isNull() )
4750 return QVariant();
4751
4752 return QVariant::fromValue( fGeom.isEmpty() );
4753}
4754
4755static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4756{
4757 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4758 return QVariant::fromValue( true );
4759
4760 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4761 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4762}
4763
4764static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4765{
4766 if ( values.length() < 2 || values.length() > 3 )
4767 return QVariant();
4768
4769 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4770 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4771
4772 if ( fGeom.isNull() || sGeom.isNull() )
4773 return QVariant();
4774
4775 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4776
4777 if ( values.length() == 2 )
4778 {
4779 //two geometry arguments, return relation
4780 QString result = engine->relate( sGeom.constGet() );
4781 return QVariant::fromValue( result );
4782 }
4783 else
4784 {
4785 //three arguments, test pattern
4786 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4787 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4788 return QVariant::fromValue( result );
4789 }
4790}
4791
4792static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4793{
4794 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4795 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4796 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4797}
4798static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4799{
4800 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4801 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4802 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4803}
4804static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4805{
4806 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4807 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4808 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4809}
4810static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4811{
4812 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4813 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4814 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4815}
4816static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4817{
4818 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4819 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4820 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4821}
4822static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4823{
4824 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4825 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4826 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
4827}
4828static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4829{
4830 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4831 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4832 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
4833}
4834static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4835{
4836 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4837 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4838 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
4839}
4840
4841static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4842{
4843 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4844 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4845 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4846 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
4847 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
4848 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
4849
4851 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
4852 capStyle = Qgis::EndCapStyle::Flat;
4853 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
4854 capStyle = Qgis::EndCapStyle::Square;
4855
4857 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
4858 joinStyle = Qgis::JoinStyle::Miter;
4859 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
4860 joinStyle = Qgis::JoinStyle::Bevel;
4861
4862 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
4863 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4864 return result;
4865}
4866
4867static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4868{
4869 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4870 const QgsGeometry reoriented = fGeom.forceRHR();
4871 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4872}
4873
4874static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4875{
4876 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4877 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
4878 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4879}
4880
4881static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4882{
4883 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4884 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
4885 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
4886}
4887
4888static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4889{
4890 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4891 const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( fGeom.constGet() );
4892 if ( !pt && fGeom.isMultipart() )
4893 {
4894 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
4895 {
4896 if ( collection->numGeometries() == 1 )
4897 {
4898 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4899 }
4900 }
4901 }
4902
4903 if ( !pt )
4904 {
4905 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
4906 return QVariant();
4907 }
4908
4909 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4910 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4911 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4912 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
4913
4914 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
4915 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4916 return result;
4917}
4918
4919static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4920{
4921 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4922 if ( fGeom.type() != Qgis::GeometryType::Line )
4923 {
4924 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
4925 return QVariant();
4926 }
4927
4928 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4929 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4930 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4931
4932 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
4933 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4934 return result;
4935}
4936
4937static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4938{
4939 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4940 if ( fGeom.type() != Qgis::GeometryType::Line )
4941 {
4942 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
4943 return QVariant();
4944 }
4945
4946 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
4947
4948 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
4949 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4950 return result;
4951}
4952
4953static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4954{
4955 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4956 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4957 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4958 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4959 if ( joinInt < 1 || joinInt > 3 )
4960 return QVariant();
4961 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4962
4963 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4964
4965 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
4966 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4967 return result;
4968}
4969
4970static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4971{
4972 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4973 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4974 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4975
4976 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
4977 if ( joinInt < 1 || joinInt > 3 )
4978 return QVariant();
4979 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
4980
4981 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4982
4983 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
4984 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4985 return result;
4986}
4987
4988static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4989{
4990 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4991 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4992 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4993
4994 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
4995 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4996 return result;
4997}
4998
4999static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5000{
5001 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5002 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5003 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5004 fGeom.translate( dx, dy );
5005 return QVariant::fromValue( fGeom );
5006}
5007
5008static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5009{
5010 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5011 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5012 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5013 : QgsGeometry();
5014 const bool perPart = values.value( 3 ).toBool();
5015
5016 if ( center.isNull() && perPart && fGeom.isMultipart() )
5017 {
5018 // no explicit center, rotating per part
5019 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5020 // the result is equivalent to setting perPart as false anyway)
5021 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5022 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5023 {
5024 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5025 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5026 t.rotate( -rotation );
5027 t.translate( -partCenter.x(), -partCenter.y() );
5028 ( *it )->transform( t );
5029 }
5030 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5031 }
5032 else
5033 {
5034 QgsPointXY pt;
5035 if ( center.isEmpty() )
5036 {
5037 // if center wasn't specified, use bounding box centroid
5038 pt = fGeom.boundingBox().center();
5039 }
5041 {
5042 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5043 return QVariant();
5044 }
5045 else
5046 {
5047 pt = QgsPointXY( *qgsgeometry_cast< const QgsPoint * >( center.constGet()->simplifiedTypeRef() ) );
5048 }
5049
5050 fGeom.rotate( rotation, pt );
5051 return QVariant::fromValue( fGeom );
5052 }
5053}
5054
5055static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5056{
5057 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5058 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5059 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5060 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5061 : QgsGeometry();
5062
5063 QgsPointXY pt;
5064 if ( center.isNull() )
5065 {
5066 // if center wasn't specified, use bounding box centroid
5067 pt = fGeom.boundingBox().center();
5068 }
5070 {
5071 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5072 return QVariant();
5073 }
5074 else
5075 {
5076 pt = center.asPoint();
5077 }
5078
5079 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5080 t.scale( xScale, yScale );
5081 t.translate( -pt.x(), -pt.y() );
5082 fGeom.transform( t );
5083 return QVariant::fromValue( fGeom );
5084}
5085
5086static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5087{
5088 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5089 if ( fGeom.isNull() )
5090 {
5091 return QVariant();
5092 }
5093
5094 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5095 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5096
5097 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5098
5099 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5100 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5101
5102 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5103 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5104 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5105 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5106
5107 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5108 {
5109 fGeom.get()->addZValue( 0 );
5110 }
5111 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5112 {
5113 fGeom.get()->addMValue( 0 );
5114 }
5115
5116 QTransform transform;
5117 transform.translate( deltaX, deltaY );
5118 transform.rotate( rotationZ );
5119 transform.scale( scaleX, scaleY );
5120 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5121
5122 return QVariant::fromValue( fGeom );
5123}
5124
5125
5126static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5127{
5128 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5129 QgsGeometry geom = fGeom.centroid();
5130 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5131 return result;
5132}
5133static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5134{
5135 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5136 QgsGeometry geom = fGeom.pointOnSurface();
5137 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5138 return result;
5139}
5140
5141static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5142{
5143 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5144 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5145 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5146 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5147 return result;
5148}
5149
5150static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5151{
5152 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5153 QgsGeometry geom = fGeom.convexHull();
5154 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5155 return result;
5156}
5157
5158#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5159static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5160{
5161 try
5162 {
5163 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5164 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5165 const bool allowHoles = values.value( 2 ).toBool();
5166 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5167 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5168 return result;
5169 }
5170 catch ( QgsCsException &cse )
5171 {
5172 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5173 return QVariant();
5174 }
5175}
5176#endif
5177
5178static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5179{
5180 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5181 int segments = 36;
5182 if ( values.length() == 2 )
5183 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5184 if ( segments < 0 )
5185 {
5186 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5187 return QVariant();
5188 }
5189
5190 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5191 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5192 return result;
5193}
5194
5195static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5196{
5197 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5199 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5200 return result;
5201}
5202
5203static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5204{
5205 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5206
5207 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5208 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5209 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5210
5211 double area, angle, width, height;
5212 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5213
5214 if ( geom.isNull() )
5215 {
5216 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5217 return QVariant();
5218 }
5219 return angle;
5220}
5221
5222static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5223{
5224 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5225 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5226 QgsGeometry geom = fGeom.difference( sGeom );
5227 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5228 return result;
5229}
5230
5231static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5232{
5233 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5234 if ( fGeom.isNull() )
5235 return QVariant();
5236
5237 QVariant result;
5238 if ( !fGeom.isMultipart() )
5239 {
5240 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5241 if ( !curve )
5242 return QVariant();
5243
5244 QgsCurve *reversed = curve->reversed();
5245 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5246 }
5247 else
5248 {
5249 const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection *>( fGeom.constGet() );
5250 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5251 for ( int i = 0; i < collection->numGeometries(); ++i )
5252 {
5253 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5254 {
5255 reversed->addGeometry( curve->reversed() );
5256 }
5257 else
5258 {
5259 reversed->addGeometry( collection->geometryN( i )->clone() );
5260 }
5261 }
5262 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5263 }
5264 return result;
5265}
5266
5267static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5268{
5269 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5270 if ( fGeom.isNull() )
5271 return QVariant();
5272
5273 const QgsCurvePolygon *curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( fGeom.constGet() );
5274 if ( !curvePolygon && fGeom.isMultipart() )
5275 {
5276 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom.constGet() ) )
5277 {
5278 if ( collection->numGeometries() == 1 )
5279 {
5280 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5281 }
5282 }
5283 }
5284
5285 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5286 return QVariant();
5287
5288 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5289 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5290 return result;
5291}
5292
5293static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5294{
5295 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5296 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5297 return QVariant( fGeom.distance( sGeom ) );
5298}
5299
5300static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5301{
5302 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5303 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5304
5305 double res = -1;
5306 if ( values.length() == 3 && values.at( 2 ).isValid() )
5307 {
5308 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5309 densify = std::clamp( densify, 0.0, 1.0 );
5310 res = g1.hausdorffDistanceDensify( g2, densify );
5311 }
5312 else
5313 {
5314 res = g1.hausdorffDistance( g2 );
5315 }
5316
5317 return res > -1 ? QVariant( res ) : QVariant();
5318}
5319
5320static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5321{
5322 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5323 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5324 QgsGeometry geom = fGeom.intersection( sGeom );
5325 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5326 return result;
5327}
5328static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5329{
5330 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5331 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5332 QgsGeometry geom = fGeom.symDifference( sGeom );
5333 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5334 return result;
5335}
5336static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5337{
5338 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5339 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5340 QgsGeometry geom = fGeom.combine( sGeom );
5341 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5342 return result;
5343}
5344
5345static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5346{
5347 if ( values.length() < 1 || values.length() > 2 )
5348 return QVariant();
5349
5350 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5351 int prec = 8;
5352 if ( values.length() == 2 )
5353 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5354 QString wkt = fGeom.asWkt( prec );
5355 return QVariant( wkt );
5356}
5357
5358static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5359{
5360 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5361 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5362}
5363
5364static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5365{
5366 if ( values.length() != 2 )
5367 {
5368 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5369 return QVariant();
5370 }
5371
5372 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5373 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5374
5375 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5376 if ( !pt1 && fGeom1.isMultipart() )
5377 {
5378 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5379 {
5380 if ( collection->numGeometries() == 1 )
5381 {
5382 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5383 }
5384 }
5385 }
5386
5387 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5388 if ( !pt2 && fGeom2.isMultipart() )
5389 {
5390 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5391 {
5392 if ( collection->numGeometries() == 1 )
5393 {
5394 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5395 }
5396 }
5397 }
5398
5399 if ( !pt1 || !pt2 )
5400 {
5401 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5402 return QVariant();
5403 }
5404
5405 // Code from PostGIS
5406 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5407 {
5408 if ( pt1->y() < pt2->y() )
5409 return 0.0;
5410 else if ( pt1->y() > pt2->y() )
5411 return M_PI;
5412 else
5413 return 0;
5414 }
5415
5416 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5417 {
5418 if ( pt1->x() < pt2->x() )
5419 return M_PI_2;
5420 else if ( pt1->x() > pt2->x() )
5421 return M_PI + ( M_PI_2 );
5422 else
5423 return 0;
5424 }
5425
5426 if ( pt1->x() < pt2->x() )
5427 {
5428 if ( pt1->y() < pt2->y() )
5429 {
5430 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5431 }
5432 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5433 {
5434 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5435 + ( M_PI_2 );
5436 }
5437 }
5438
5439 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5440 {
5441 if ( pt1->y() > pt2->y() )
5442 {
5443 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5444 + M_PI;
5445 }
5446 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5447 {
5448 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5449 + ( M_PI + ( M_PI_2 ) );
5450 }
5451 }
5452}
5453
5454static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5455{
5456 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5457 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5458 QString sourceCrs = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5459 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5460
5461 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5462 {
5463 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5464 return QVariant();
5465 }
5466
5467 const QgsPointXY point1 = geom1.asPoint();
5468 const QgsPointXY point2 = geom2.asPoint();
5469 if ( point1.isEmpty() || point2.isEmpty() )
5470 {
5471 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5472 return QVariant();
5473 }
5474
5476 if ( context )
5477 {
5478 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5479
5480 if ( sourceCrs.isEmpty() )
5481 {
5482 sourceCrs = context->variable( QStringLiteral( "layer_crs" ) ).toString();
5483 }
5484
5485 if ( ellipsoid.isEmpty() )
5486 {
5487 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5488 }
5489 }
5490
5492 if ( !sCrs.isValid() )
5493 {
5494 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5495 return QVariant();
5496 }
5497
5498 QgsDistanceArea da;
5499 da.setSourceCrs( sCrs, tContext );
5500 if ( !da.setEllipsoid( ellipsoid ) )
5501 {
5502 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5503 return QVariant();
5504 }
5505
5506 try
5507 {
5508 const double bearing = da.bearing( point1, point2 );
5509 if ( std::isfinite( bearing ) )
5510 {
5511 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5512 }
5513 }
5514 catch ( QgsCsException &cse )
5515 {
5516 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5517 return QVariant();
5518 }
5519 return QVariant();
5520}
5521
5522static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5523{
5524 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5525
5527 {
5528 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5529 return QVariant();
5530 }
5531
5532 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5533 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5534 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5535
5536 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5537 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5538
5539 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5540}
5541
5542static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5543{
5544 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5545 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5546
5547 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5548 if ( !pt1 && fGeom1.isMultipart() )
5549 {
5550 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom1.constGet() ) )
5551 {
5552 if ( collection->numGeometries() == 1 )
5553 {
5554 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5555 }
5556 }
5557 }
5558 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5559 if ( !pt2 && fGeom2.isMultipart() )
5560 {
5561 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( fGeom2.constGet() ) )
5562 {
5563 if ( collection->numGeometries() == 1 )
5564 {
5565 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5566 }
5567 }
5568 }
5569
5570 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5571 !pt1 || !pt2 )
5572 {
5573 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5574 return QVariant();
5575 }
5576
5577 return pt1->inclination( *pt2 );
5578
5579}
5580
5581static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5582{
5583 if ( values.length() != 3 )
5584 return QVariant();
5585
5586 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5587 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5588 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5589
5590 QgsGeometry geom = fGeom.extrude( x, y );
5591
5592 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVa