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