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