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