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