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 if ( it == map.constBegin() )
5524 {
5525 if ( step != 0.0 )
5526 stops << QgsGradientStop( step, colors.last() );
5527 }
5528 else if ( it == map.constEnd() )
5529 {
5530 if ( step != 1.0 )
5531 stops << QgsGradientStop( step, colors.last() );
5532 }
5533 else
5534 {
5535 stops << QgsGradientStop( step, colors.last() );
5536 }
5537 }
5538 bool discrete = values.at( 1 ).toBool();
5539
5540 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
5541}
5542
5543static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5544{
5545 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5546 if ( ! color.isValid() )
5547 {
5548 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5549 return QVariant();
5550 }
5551
5552 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5553 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5554 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
5555 color.setRed( value );
5556 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
5557 color.setGreen( value );
5558 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
5559 color.setBlue( value );
5560 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
5561 color.setAlpha( value );
5562 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
5563 color.setHsv( value, color.hsvSaturation(), color.value(), color.alpha() );
5564 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
5565 color.setHsvF( color.hsvHueF(), value / 100.0, color.valueF(), color.alphaF() );
5566 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
5567 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), value / 100.0, color.alphaF() );
5568 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
5569 color.setHsl( value, color.hslSaturation(), color.lightness(), color.alpha() );
5570 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
5571 color.setHslF( color.hslHueF(), value / 100.0, color.lightnessF(), color.alphaF() );
5572 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
5573 color.setHslF( color.hslHueF(), color.hslSaturationF(), value / 100.0, color.alphaF() );
5574 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
5575 color.setCmykF( value / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
5576 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
5577 color.setCmykF( color.cyanF(), value / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
5578 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
5579 color.setCmykF( color.cyanF(), color.magentaF(), value / 100.0, color.blackF(), color.alphaF() );
5580 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
5581 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), value / 100.0, color.alphaF() );
5582 else
5583 {
5584 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
5585 return QVariant();
5586 }
5587 return QgsSymbolLayerUtils::encodeColor( color );
5588}
5589
5590static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5591{
5592 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5593 if ( ! color.isValid() )
5594 {
5595 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5596 return QVariant();
5597 }
5598
5599 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5600
5601 return QgsSymbolLayerUtils::encodeColor( color );
5602}
5603
5604static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5605{
5606 QColor color = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
5607 if ( ! color.isValid() )
5608 {
5609 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( values.at( 0 ).toString() ) );
5610 return QVariant();
5611 }
5612
5613 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
5614
5615 return QgsSymbolLayerUtils::encodeColor( color );
5616}
5617
5618static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5619{
5620 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
5621 QgsGeometry geom = feat.geometry();
5622 if ( !geom.isNull() )
5623 return QVariant::fromValue( geom );
5624 return QVariant();
5625}
5626
5627static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5628{
5629 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5630 QString sAuthId = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5631 QString dAuthId = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5632
5634 if ( ! s.isValid() )
5635 return QVariant::fromValue( fGeom );
5637 if ( ! d.isValid() )
5638 return QVariant::fromValue( fGeom );
5639
5641 if ( context )
5642 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5643 QgsCoordinateTransform t( s, d, tContext );
5644 try
5645 {
5647 return QVariant::fromValue( fGeom );
5648 }
5649 catch ( QgsCsException &cse )
5650 {
5651 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
5652 return QVariant();
5653 }
5654 return QVariant();
5655}
5656
5657
5658static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5659{
5660 QVariant result;
5661 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
5662 if ( vl )
5663 {
5664 QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
5665
5667 req.setFilterFid( fid );
5668 req.setTimeout( 10000 );
5669 req.setRequestMayBeNested( true );
5670 if ( context )
5671 req.setFeedback( context->feedback() );
5672 QgsFeatureIterator fIt = vl->getFeatures( req );
5673
5674 QgsFeature fet;
5675 if ( fIt.nextFeature( fet ) )
5676 result = QVariant::fromValue( fet );
5677 }
5678
5679 return result;
5680}
5681
5682static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5683{
5684 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
5685
5686 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), parent );
5687
5688 //no layer found
5689 if ( !featureSource )
5690 {
5691 return QVariant();
5692 }
5694 QString cacheValueKey;
5695 if ( values.at( 1 ).type() == QVariant::Map )
5696 {
5697 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
5698
5699 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
5700 QString filterString;
5701 for ( ; i != attributeMap.constEnd(); ++i )
5702 {
5703 if ( !filterString.isEmpty() )
5704 {
5705 filterString.append( " AND " );
5706 }
5707 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
5708 }
5709 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
5710 if ( context && context->hasCachedValue( cacheValueKey ) )
5711 {
5712 return context->cachedValue( cacheValueKey );
5713 }
5714 req.setFilterExpression( filterString );
5715 }
5716 else
5717 {
5718 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5719 int attributeId = featureSource->fields().lookupField( attribute );
5720 if ( attributeId == -1 )
5721 {
5722 return QVariant();
5723 }
5724
5725 const QVariant &attVal = values.at( 2 );
5726
5727 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
5728 if ( context && context->hasCachedValue( cacheValueKey ) )
5729 {
5730 return context->cachedValue( cacheValueKey );
5731 }
5732
5734 }
5735 req.setLimit( 1 );
5736 req.setTimeout( 10000 );
5737 req.setRequestMayBeNested( true );
5738 if ( context )
5739 req.setFeedback( context->feedback() );
5740 if ( !parent->needsGeometry() )
5741 {
5743 }
5744 QgsFeatureIterator fIt = featureSource->getFeatures( req );
5745
5746 QgsFeature fet;
5747 QVariant res;
5748 if ( fIt.nextFeature( fet ) )
5749 {
5750 res = QVariant::fromValue( fet );
5751 }
5752
5753 if ( context )
5754 context->setCachedValue( cacheValueKey, res );
5755 return res;
5756}
5757
5758static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
5759{
5760 QVariant result;
5761 QString fieldName;
5762
5763 if ( context )
5764 {
5765 if ( !values.isEmpty() )
5766 {
5767 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
5768 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
5769 fieldName = col->name();
5770 else if ( values.size() == 2 )
5771 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5772 }
5773
5774 QVariant value = values.at( 0 );
5775
5776 const QgsFields fields = context->fields();
5777 int fieldIndex = fields.lookupField( fieldName );
5778
5779 if ( fieldIndex == -1 )
5780 {
5781 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5782 }
5783 else
5784 {
5785 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
5786
5787 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
5788 if ( context->hasCachedValue( cacheValueKey ) )
5789 {
5790 return context->cachedValue( cacheValueKey );
5791 }
5792
5793 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
5795
5796 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
5797
5798 QVariant cache;
5799 if ( !context->hasCachedValue( cacheKey ) )
5800 {
5801 cache = formatter->createCache( layer, fieldIndex, setup.config() );
5802 context->setCachedValue( cacheKey, cache );
5803 }
5804 else
5805 cache = context->cachedValue( cacheKey );
5806
5807 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
5808
5809 context->setCachedValue( cacheValueKey, result );
5810 }
5811 }
5812 else
5813 {
5814 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
5815 }
5816
5817 return result;
5818}
5819
5820static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5821{
5822 const QVariant data = values.at( 0 );
5823 const QMimeDatabase db;
5824 return db.mimeTypeForData( data.toByteArray() ).name();
5825}
5826
5827static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5828{
5829 QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5830
5831 if ( !layer )
5832 return QVariant();
5833
5834 // here, we always prefer the layer metadata values over the older server-specific published values
5835 QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5836 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
5837 return layer->name();
5838 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
5839 return layer->id();
5840 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
5841 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->title();
5842 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
5843 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->abstract();
5844 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
5845 {
5846 QStringList keywords;
5847 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
5848 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
5849 {
5850 keywords.append( it.value() );
5851 }
5852 if ( !keywords.isEmpty() )
5853 return keywords;
5854 return layer->keywordList();
5855 }
5856 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
5857 return layer->dataUrl();
5858 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
5859 {
5860 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->attribution() );
5861 }
5862 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
5863 return layer->attributionUrl();
5864 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
5865 return layer->publicSource();
5866 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
5867 return layer->minimumScale();
5868 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
5869 return layer->maximumScale();
5870 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
5871 return layer->isEditable();
5872 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
5873 return layer->crs().authid();
5874 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
5875 return layer->crs().toProj();
5876 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
5877 return layer->crs().description();
5878 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
5879 {
5880 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
5881 QVariant result = QVariant::fromValue( extentGeom );
5882 return result;
5883 }
5884 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
5885 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
5886 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
5887 {
5888 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
5889 return decodedUri.value( QStringLiteral( "path" ) );
5890 }
5891 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
5892 {
5893 switch ( layer->type() )
5894 {
5896 return QCoreApplication::translate( "expressions", "Vector" );
5898 return QCoreApplication::translate( "expressions", "Raster" );
5900 return QCoreApplication::translate( "expressions", "Mesh" );
5902 return QCoreApplication::translate( "expressions", "Vector Tile" );
5904 return QCoreApplication::translate( "expressions", "Plugin" );
5906 return QCoreApplication::translate( "expressions", "Annotation" );
5908 return QCoreApplication::translate( "expressions", "Point Cloud" );
5910 return QCoreApplication::translate( "expressions", "Group" );
5911 }
5912 }
5913 else
5914 {
5915 //vector layer methods
5916 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
5917 if ( vLayer )
5918 {
5919 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
5920 return vLayer->storageType();
5921 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
5923 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
5924 return QVariant::fromValue( vLayer->featureCount() );
5925 }
5926 }
5927
5928 return QVariant();
5929}
5930
5931static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5932{
5933 QgsMapLayer *layer = QgsExpressionUtils::getMapLayer( values.at( 0 ), parent );
5934 if ( !layer )
5935 {
5936 parent->setEvalErrorString( QObject::tr( "Cannot find layer %1" ).arg( values.at( 0 ).toString() ) );
5937 return QVariant();
5938 }
5939
5940 if ( !layer->dataProvider() )
5941 {
5942 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
5943 return QVariant();
5944 }
5945
5946 const QString uriPart = values.at( 1 ).toString();
5947
5948 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
5949
5950 if ( !uriPart.isNull() )
5951 {
5952 return decodedUri.value( values.at( 1 ).toString() );
5953 }
5954 else
5955 {
5956 return decodedUri;
5957 }
5958}
5959
5960static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5961{
5962 QString layerIdOrName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5963
5964 //try to find a matching layer by name
5965 QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerIdOrName ); //search by id first
5966 if ( !layer )
5967 {
5968 QList<QgsMapLayer *> layersByName = QgsProject::instance()->mapLayersByName( layerIdOrName );
5969 if ( !layersByName.isEmpty() )
5970 {
5971 layer = layersByName.at( 0 );
5972 }
5973 }
5974
5975 if ( !layer )
5976 return QVariant();
5977
5978 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
5979 if ( !rl )
5980 return QVariant();
5981
5982 int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5983 if ( band < 1 || band > rl->bandCount() )
5984 {
5985 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer %2" ).arg( band ).arg( layerIdOrName ) );
5986 return QVariant();
5987 }
5988
5989 QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
5990 int stat = 0;
5991
5992 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
5994 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
5996 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
5998 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
6000 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
6002 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
6004 else
6005 {
6006 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
6007 return QVariant();
6008 }
6009
6010 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
6011 switch ( stat )
6012 {
6014 return stats.mean;
6016 return stats.stdDev;
6018 return stats.minimumValue;
6020 return stats.maximumValue;
6022 return stats.range;
6024 return stats.sum;
6025 }
6026 return QVariant();
6027}
6028
6029static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6030{
6031 return values;
6032}
6033
6034static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6035{
6036 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6037 bool ascending = values.value( 1 ).toBool();
6038 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
6039 return list;
6040}
6041
6042static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6043{
6044 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
6045}
6046
6047static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6048{
6049 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
6050}
6051
6052static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6053{
6054 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
6055}
6056
6057static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6058{
6059 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6060 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6061 int match = 0;
6062 for ( const auto &item : listB )
6063 {
6064 if ( listA.contains( item ) )
6065 match++;
6066 }
6067
6068 return QVariant( match == listB.count() );
6069}
6070
6071static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6072{
6073 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
6074}
6075
6076static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6077{
6078 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6079 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6080 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
6081 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
6082 return list.at( list.length() + pos );
6083 return QVariant();
6084}
6085
6086static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6087{
6088 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6089 return list.value( 0 );
6090}
6091
6092static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6093{
6094 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6095 return list.value( list.size() - 1 );
6096}
6097
6098static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6099{
6100 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6101 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6102}
6103
6104static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6105{
6106 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6107 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6108}
6109
6110static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6111{
6112 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6113 int i = 0;
6114 double total = 0.0;
6115 for ( const QVariant &item : list )
6116 {
6117 switch ( item.userType() )
6118 {
6119 case QMetaType::Int:
6120 case QMetaType::UInt:
6121 case QMetaType::LongLong:
6122 case QMetaType::ULongLong:
6123 case QMetaType::Float:
6124 case QMetaType::Double:
6125 total += item.toDouble();
6126 ++i;
6127 break;
6128 }
6129 }
6130 return i == 0 ? QVariant() : total / i;
6131}
6132
6133static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6134{
6135 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6136 QVariantList numbers;
6137 for ( const auto &item : list )
6138 {
6139 switch ( item.userType() )
6140 {
6141 case QMetaType::Int:
6142 case QMetaType::UInt:
6143 case QMetaType::LongLong:
6144 case QMetaType::ULongLong:
6145 case QMetaType::Float:
6146 case QMetaType::Double:
6147 numbers.append( item );
6148 break;
6149 }
6150 }
6151 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
6152 const int count = numbers.count();
6153 if ( count == 0 )
6154 {
6155 return QVariant();
6156 }
6157 else if ( count % 2 )
6158 {
6159 return numbers.at( count / 2 );
6160 }
6161 else
6162 {
6163 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
6164 }
6165}
6166
6167static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6168{
6169 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6170 int i = 0;
6171 double total = 0.0;
6172 for ( const QVariant &item : list )
6173 {
6174 switch ( item.userType() )
6175 {
6176 case QMetaType::Int:
6177 case QMetaType::UInt:
6178 case QMetaType::LongLong:
6179 case QMetaType::ULongLong:
6180 case QMetaType::Float:
6181 case QMetaType::Double:
6182 total += item.toDouble();
6183 ++i;
6184 break;
6185 }
6186 }
6187 return i == 0 ? QVariant() : total;
6188}
6189
6190static QVariant convertToSameType( const QVariant &value, QVariant::Type type )
6191{
6192 QVariant result = value;
6193 result.convert( static_cast<int>( type ) );
6194 return result;
6195}
6196
6197static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6198{
6199 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6200 QHash< QVariant, int > hash;
6201 for ( const auto &item : list )
6202 {
6203 ++hash[item];
6204 }
6205 const QList< int > occurrences = hash.values();
6206 if ( occurrences.empty() )
6207 return QVariantList();
6208
6209 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6210
6211 const QString option = values.at( 1 ).toString();
6212 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6213 {
6214 return convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() );
6215 }
6216 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6217 {
6218 if ( hash.isEmpty() )
6219 return QVariant();
6220
6221 return QVariant( hash.keys( maxValue ).first() );
6222 }
6223 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6224 {
6225 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), values.at( 0 ).type() ), context, parent, node );
6226 }
6227 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
6228 {
6229 if ( maxValue * 2 <= list.size() )
6230 return QVariant();
6231
6232 return QVariant( hash.keys( maxValue ).first() );
6233 }
6234 else
6235 {
6236 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6237 return QVariant();
6238 }
6239}
6240
6241static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6242{
6243 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6244 QHash< QVariant, int > hash;
6245 for ( const auto &item : list )
6246 {
6247 ++hash[item];
6248 }
6249 const QList< int > occurrences = hash.values();
6250 if ( occurrences.empty() )
6251 return QVariantList();
6252
6253 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
6254
6255 const QString option = values.at( 1 ).toString();
6256 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
6257 {
6258 return convertToSameType( hash.keys( minValue ), values.at( 0 ).type() );
6259 }
6260 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
6261 {
6262 if ( hash.isEmpty() )
6263 return QVariant();
6264
6265 return QVariant( hash.keys( minValue ).first() );
6266 }
6267 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
6268 {
6269 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), values.at( 0 ).type() ), context, parent, node );
6270 }
6271 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
6272 {
6273 if ( hash.keys().isEmpty() )
6274 return QVariant();
6275
6276 // Remove the majority, all others are minority
6277 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
6278 if ( maxValue * 2 > list.size() )
6279 hash.remove( hash.key( maxValue ) );
6280
6281 return convertToSameType( hash.keys(), values.at( 0 ).type() );
6282 }
6283 else
6284 {
6285 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
6286 return QVariant();
6287 }
6288}
6289
6290static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6291{
6292 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6293 list.append( values.at( 1 ) );
6294 return convertToSameType( list, values.at( 0 ).type() );
6295}
6296
6297static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6298{
6299 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6300 list.prepend( values.at( 1 ) );
6301 return convertToSameType( list, values.at( 0 ).type() );
6302}
6303
6304static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6305{
6306 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6307 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
6308 return convertToSameType( list, values.at( 0 ).type() );
6309}
6310
6311static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6312{
6313 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6314 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6315 if ( position < 0 )
6316 position = position + list.length();
6317 if ( position >= 0 && position < list.length() )
6318 list.removeAt( position );
6319 return convertToSameType( list, values.at( 0 ).type() );
6320}
6321
6322static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6323{
6324 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6325 list.removeAll( values.at( 1 ) );
6326 return convertToSameType( list, values.at( 0 ).type() );
6327}
6328
6329static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6330{
6331 if ( values.count() == 2 && values.at( 1 ).type() == QVariant::Map )
6332 {
6333 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6334
6335 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6336 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6337 {
6338 int index = list.indexOf( it.key() );
6339 while ( index >= 0 )
6340 {
6341 list.replace( index, it.value() );
6342 index = list.indexOf( it.key() );
6343 }
6344 }
6345
6346 return convertToSameType( list, values.at( 0 ).type() );
6347 }
6348 else if ( values.count() == 3 )
6349 {
6350 QVariantList before;
6351 QVariantList after;
6352 bool isSingleReplacement = false;
6353
6354 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).type() != QVariant::StringList )
6355 {
6356 before = QVariantList() << values.at( 1 );
6357 }
6358 else
6359 {
6360 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6361 }
6362
6363 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
6364 {
6365 after = QVariantList() << values.at( 2 );
6366 isSingleReplacement = true;
6367 }
6368 else
6369 {
6370 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
6371 }
6372
6373 if ( !isSingleReplacement && before.length() != after.length() )
6374 {
6375 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
6376 return QVariant();
6377 }
6378
6379 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6380 for ( int i = 0; i < before.length(); i++ )
6381 {
6382 int index = list.indexOf( before.at( i ) );
6383 while ( index >= 0 )
6384 {
6385 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
6386 index = list.indexOf( before.at( i ) );
6387 }
6388 }
6389
6390 return convertToSameType( list, values.at( 0 ).type() );
6391 }
6392 else
6393 {
6394 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
6395 return QVariant();
6396 }
6397}
6398
6399static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6400{
6401 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6402 QVariantList list_new;
6403
6404 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
6405 {
6406 while ( list.removeOne( cur ) )
6407 {
6408 list_new.append( cur );
6409 }
6410 }
6411
6412 list_new.append( list );
6413
6414 return convertToSameType( list_new, values.at( 0 ).type() );
6415}
6416
6417static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6418{
6419 QVariantList list;
6420 for ( const QVariant &cur : values )
6421 {
6422 list += QgsExpressionUtils::getListValue( cur, parent );
6423 }
6424 return convertToSameType( list, values.at( 0 ).type() );
6425}
6426
6427static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6428{
6429 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6430 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6431 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6432 int slice_length = 0;
6433 // negative positions means positions taken relative to the end of the array
6434 if ( start_pos < 0 )
6435 {
6436 start_pos = list.length() + start_pos;
6437 }
6438 if ( end_pos >= 0 )
6439 {
6440 slice_length = end_pos - start_pos + 1;
6441 }
6442 else
6443 {
6444 slice_length = list.length() + end_pos - start_pos + 1;
6445 }
6446 //avoid negative lengths in QList.mid function
6447 if ( slice_length < 0 )
6448 {
6449 slice_length = 0;
6450 }
6451 list = list.mid( start_pos, slice_length );
6452 return list;
6453}
6454
6455static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6456{
6457 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6458 std::reverse( list.begin(), list.end() );
6459 return list;
6460}
6461
6462static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6463{
6464 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6465 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
6466 for ( const QVariant &cur : array2 )
6467 {
6468 if ( array1.contains( cur ) )
6469 return QVariant( true );
6470 }
6471 return QVariant( false );
6472}
6473
6474static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6475{
6476 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6477
6478 QVariantList distinct;
6479
6480 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
6481 {
6482 if ( !distinct.contains( *it ) )
6483 {
6484 distinct += ( *it );
6485 }
6486 }
6487
6488 return distinct;
6489}
6490
6491static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6492{
6493 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
6494 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6495 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6496
6497 QString str;
6498
6499 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
6500 {
6501 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
6502 if ( it != ( array.constEnd() - 1 ) )
6503 {
6504 str += delimiter;
6505 }
6506 }
6507
6508 return QVariant( str );
6509}
6510
6511static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6512{
6513 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6514 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6515 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6516
6517 QStringList list = str.split( delimiter );
6518 QVariantList array;
6519
6520 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
6521 {
6522 array += ( !( *it ).isEmpty() ) ? *it : empty;
6523 }
6524
6525 return array;
6526}
6527
6528static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6529{
6530 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6531 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
6532 if ( document.isNull() )
6533 return QVariant();
6534
6535 return document.toVariant();
6536}
6537
6538static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6539{
6540 Q_UNUSED( parent )
6541 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
6542 return QString( document.toJson( QJsonDocument::Compact ) );
6543}
6544
6545static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6546{
6547 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6548 if ( str.isEmpty() )
6549 return QVariantMap();
6550 str = str.trimmed();
6551
6552 return QgsHstoreUtils::parse( str );
6553}
6554
6555static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6556{
6557 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6558 return QgsHstoreUtils::build( map );
6559}
6560
6561static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6562{
6563 QVariantMap result;
6564 for ( int i = 0; i + 1 < values.length(); i += 2 )
6565 {
6566 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
6567 }
6568 return result;
6569}
6570
6571static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6572{
6573 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6574 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6575 QVariantMap resultMap;
6576
6577 for ( auto it = map.cbegin(); it != map.cend(); it++ )
6578 {
6579 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
6580 }
6581
6582 return resultMap;
6583}
6584
6585static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6586{
6587 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
6588}
6589
6590static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6591{
6592 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
6593}
6594
6595static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6596{
6597 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6598 map.remove( values.at( 1 ).toString() );
6599 return map;
6600}
6601
6602static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6603{
6604 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6605 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
6606 return map;
6607}
6608
6609static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6610{
6611 QVariantMap result;
6612 for ( const QVariant &cur : values )
6613 {
6614 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
6615 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
6616 result.insert( it.key(), it.value() );
6617 }
6618 return result;
6619}
6620
6621static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6622{
6623 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
6624}
6625
6626static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6627{
6628 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
6629}
6630
6631static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6632{
6633 const QString envVarName = values.at( 0 ).toString();
6634 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
6635 return QVariant();
6636
6637 return QProcessEnvironment::systemEnvironment().value( envVarName );
6638}
6639
6640static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6641{
6642 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6643 if ( parent->hasEvalError() )
6644 {
6645 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
6646 return QVariant();
6647 }
6648 return QFileInfo( file ).completeBaseName();
6649}
6650
6651static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6652{
6653 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6654 if ( parent->hasEvalError() )
6655 {
6656 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
6657 return QVariant();
6658 }
6659 return QFileInfo( file ).completeSuffix();
6660}
6661
6662static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6663{
6664 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6665 if ( parent->hasEvalError() )
6666 {
6667 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
6668 return QVariant();
6669 }
6670 return QFileInfo::exists( file );
6671}
6672
6673static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6674{
6675 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6676 if ( parent->hasEvalError() )
6677 {
6678 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
6679 return QVariant();
6680 }
6681 return QFileInfo( file ).fileName();
6682}
6683
6684static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6685{
6686 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6687 if ( parent->hasEvalError() )
6688 {
6689 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
6690 return QVariant();
6691 }
6692 return QFileInfo( file ).isFile();
6693}
6694
6695static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6696{
6697 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6698 if ( parent->hasEvalError() )
6699 {
6700 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
6701 return QVariant();
6702 }
6703 return QFileInfo( file ).isDir();
6704}
6705
6706static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6707{
6708 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6709 if ( parent->hasEvalError() )
6710 {
6711 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
6712 return QVariant();
6713 }
6714 return QDir::toNativeSeparators( QFileInfo( file ).path() );
6715}
6716
6717static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6718{
6719 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
6720 if ( parent->hasEvalError() )
6721 {
6722 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
6723 return QVariant();
6724 }
6725 return QFileInfo( file ).size();
6726}
6727
6728static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
6729{
6730 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
6731}
6732
6733static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6734{
6735 QVariant hash;
6736 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6737 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
6738
6739 if ( method == QLatin1String( "md4" ) )
6740 {
6741 hash = fcnHash( str, QCryptographicHash::Md4 );
6742 }
6743 else if ( method == QLatin1String( "md5" ) )
6744 {
6745 hash = fcnHash( str, QCryptographicHash::Md5 );
6746 }
6747 else if ( method == QLatin1String( "sha1" ) )
6748 {
6749 hash = fcnHash( str, QCryptographicHash::Sha1 );
6750 }
6751 else if ( method == QLatin1String( "sha224" ) )
6752 {
6753 hash = fcnHash( str, QCryptographicHash::Sha224 );
6754 }
6755 else if ( method == QLatin1String( "sha256" ) )
6756 {
6757 hash = fcnHash( str, QCryptographicHash::Sha256 );
6758 }
6759 else if ( method == QLatin1String( "sha384" ) )
6760 {
6761 hash = fcnHash( str, QCryptographicHash::Sha384 );
6762 }
6763 else if ( method == QLatin1String( "sha512" ) )
6764 {
6765 hash = fcnHash( str, QCryptographicHash::Sha512 );
6766 }
6767 else if ( method == QLatin1String( "sha3_224" ) )
6768 {
6769 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
6770 }
6771 else if ( method == QLatin1String( "sha3_256" ) )
6772 {
6773 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
6774 }
6775 else if ( method == QLatin1String( "sha3_384" ) )
6776 {
6777 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
6778 }
6779 else if ( method == QLatin1String( "sha3_512" ) )
6780 {
6781 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
6782 }
6783 else if ( method == QLatin1String( "keccak_224" ) )
6784 {
6785 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
6786 }
6787 else if ( method == QLatin1String( "keccak_256" ) )
6788 {
6789 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
6790 }
6791 else if ( method == QLatin1String( "keccak_384" ) )
6792 {
6793 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
6794 }
6795 else if ( method == QLatin1String( "keccak_512" ) )
6796 {
6797 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
6798 }
6799 else
6800 {
6801 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
6802 }
6803 return hash;
6804}
6805
6806static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6807{
6808 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
6809}
6810
6811static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6812{
6813 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
6814}
6815
6816static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6817{
6818 const QByteArray input = values.at( 0 ).toByteArray();
6819 return QVariant( QString( input.toBase64() ) );
6820}
6821
6822static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6823{
6824 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6825 QUrlQuery query;
6826 for ( auto it = map.cbegin(); it != map.cend(); it++ )
6827 {
6828 query.addQueryItem( it.key(), it.value().toString() );
6829 }
6830 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
6831}
6832
6833static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6834{
6835 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6836 const QByteArray base64 = value.toLocal8Bit();
6837 const QByteArray decoded = QByteArray::fromBase64( base64 );
6838 return QVariant( decoded );
6839}
6840
6841typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
6842
6843static QVariant executeGeomOverlay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const RelationFunction &relationFunction, bool invert = false, double bboxGrow = 0, bool isNearestFunc = false, bool isIntersectsFunc = false )
6844{
6845
6846 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
6847 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, parent );
6848
6849 QgsFeatureRequest request;
6850 request.setTimeout( 10000 );
6851 request.setRequestMayBeNested( true );
6852 request.setFeedback( context->feedback() );
6853
6854 // First parameter is the overlay layer
6855 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6857
6858 const bool layerCanBeCached = node->isStatic( parent, context );
6859 QVariant targetLayerValue = node->eval( parent, context );
6861
6862 // Second parameter is the expression to evaluate (or null for testonly)
6863 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6865 QString subExpString = node->dump();
6866
6867 bool testOnly = ( subExpString == "NULL" );
6868 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, parent );
6869 if ( !targetLayer ) // No layer, no joy
6870 {
6871 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
6872 return QVariant();
6873 }
6874
6875 // Third parameter is the filtering expression
6876 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6878 QString filterString = node->dump();
6879 if ( filterString != "NULL" )
6880 {
6881 request.setFilterExpression( filterString ); //filter cached features
6882 }
6883
6884 // Fourth parameter is the limit
6885 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6887 QVariant limitValue = node->eval( parent, context );
6889 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
6890
6891 // Fifth parameter (for nearest only) is the max distance
6892 double max_distance = 0;
6893 if ( isNearestFunc ) //maxdistance param handling
6894 {
6895 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
6897 QVariant distanceValue = node->eval( parent, context );
6899 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
6900 }
6901
6902 // Fifth or sixth (for nearest only) parameter is the cache toggle
6903 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
6905 QVariant cacheValue = node->eval( parent, context );
6907 bool cacheEnabled = cacheValue.toBool();
6908
6909 // Sixth parameter (for intersects only) is the min overlap (area or length)
6910 // Seventh parameter (for intersects only) is the min inscribed circle radius
6911 // Eighth parameter (for intersects only) is the return_details
6912 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
6913 double minOverlap { -1 };
6914 double minInscribedCircleRadius { -1 };
6915 bool returnDetails = false; //#spellok
6916 bool sortByMeasure = false;
6917 bool sortAscending = false;
6918 bool requireMeasures = false;
6919 bool overlapOrRadiusFilter = false;
6920 if ( isIntersectsFunc )
6921 {
6922
6923 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6925 const QVariant minOverlapValue = node->eval( parent, context );
6927 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
6928 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
6930 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
6932 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
6933 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
6934 // Return measures is only effective when an expression is set
6935 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
6936 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
6937 // Sort by measures is only effective when an expression is set
6938 const QString sorting { node->eval( parent, context ).toString().toLower() };
6939 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
6940 sortAscending = sorting.startsWith( "asc" );
6941 requireMeasures = sortByMeasure || returnDetails; //#spellok
6942 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
6943 }
6944
6945
6946 FEAT_FROM_CONTEXT( context, feat )
6947 const QgsGeometry geometry = feat.geometry();
6948
6949 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
6950 {
6951 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6952 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
6953 }
6954
6955 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
6956
6957 QgsRectangle intDomain = geometry.boundingBox();
6958 if ( bboxGrow != 0 )
6959 {
6960 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
6961 }
6962
6963 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
6964
6965 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
6966 // Otherwise, it can be toggled by the user
6967 QgsSpatialIndex spatialIndex;
6968 QgsVectorLayer *cachedTarget;
6969 QList<QgsFeature> features;
6970 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
6971 {
6972 // If the cache (local spatial index) is enabled, we materialize the whole
6973 // layer, then do the request on that layer instead.
6974 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
6975 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
6976
6977 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
6978 {
6979 cachedTarget = targetLayer->materialize( request );
6980 if ( layerCanBeCached )
6981 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
6982 }
6983 else
6984 {
6985 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
6986 }
6987
6988 if ( !context->hasCachedValue( cacheIndex ) )
6989 {
6990 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
6991 if ( layerCanBeCached )
6992 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
6993 }
6994 else
6995 {
6996 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
6997 }
6998
6999 QList<QgsFeatureId> fidsList;
7000 if ( isNearestFunc )
7001 {
7002 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
7003 }
7004 else
7005 {
7006 fidsList = spatialIndex.intersects( intDomain );
7007 }
7008
7009 QListIterator<QgsFeatureId> i( fidsList );
7010 while ( i.hasNext() )
7011 {
7012 QgsFeatureId fId2 = i.next();
7013 if ( sameLayers && feat.id() == fId2 )
7014 continue;
7015 features.append( cachedTarget->getFeature( fId2 ) );
7016 }
7017
7018 }
7019 else
7020 {
7021 // If the cache (local spatial index) is not enabled, we directly
7022 // get the features from the target layer
7023 request.setFilterRect( intDomain );
7024 QgsFeatureIterator fit = targetLayer->getFeatures( request );
7025 QgsFeature feat2;
7026 while ( fit.nextFeature( feat2 ) )
7027 {
7028 if ( sameLayers && feat.id() == feat2.id() )
7029 continue;
7030 features.append( feat2 );
7031 }
7032 }
7033
7034 QgsExpression subExpression;
7035 QgsExpressionContext subContext;
7036 if ( !testOnly )
7037 {
7038 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
7039 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
7040
7041 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
7042 {
7043 subExpression = QgsExpression( subExpString );
7045 subExpression.prepare( &subContext );
7046 }
7047 else
7048 {
7049 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
7050 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
7051 }
7052 }
7053
7054 // //////////////////////////////////////////////////////////////////
7055 // Helper functions for geometry tests
7056
7057 // Test function for linestring geometries, returns TRUE if test passes
7058 auto testLinestring = [ = ]( const QgsGeometry intersection, double & overlapValue ) -> bool
7059 {
7060 bool testResult { false };
7061 // For return measures:
7062 QVector<double> overlapValues;
7063 for ( auto it = intersection.const_parts_begin(); ! testResult && it != intersection.const_parts_end(); ++it )
7064 {
7065 const QgsCurve *geom = qgsgeometry_cast< const QgsCurve * >( *it );
7066 // Check min overlap for intersection (if set)
7067 if ( minOverlap != -1 || requireMeasures )
7068 {
7069 overlapValue = geom->length();
7070 overlapValues.append( overlapValue );
7071 if ( minOverlap != -1 )
7072 {
7073 if ( overlapValue >= minOverlap )
7074 {
7075 testResult = true;
7076 }
7077 else
7078 {
7079 continue;
7080 }
7081 }
7082 }
7083 }
7084
7085 if ( ! overlapValues.isEmpty() )
7086 {
7087 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7088 }
7089
7090 return testResult;
7091 };
7092
7093 // Test function for polygon geometries, returns TRUE if test passes
7094 auto testPolygon = [ = ]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
7095 {
7096 // overlap and inscribed circle tests must be checked both (if the values are != -1)
7097 bool testResult { false };
7098 // For return measures:
7099 QVector<double> overlapValues;
7100 QVector<double> radiusValues;
7101 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
7102 {
7103 const QgsCurvePolygon *geom = qgsgeometry_cast< const QgsCurvePolygon * >( *it );
7104 // Check min overlap for intersection (if set)
7105 if ( minOverlap != -1 || requireMeasures )
7106 {
7107 overlapValue = geom->area();
7108 overlapValues.append( geom->area() );
7109 if ( minOverlap != - 1 )
7110 {
7111 if ( overlapValue >= minOverlap )
7112 {
7113 testResult = true;
7114 }
7115 else
7116 {
7117 continue;
7118 }
7119 }
7120 }
7121
7122 // Check min inscribed circle radius for intersection (if set)
7123 if ( minInscribedCircleRadius != -1 || requireMeasures )
7124 {
7125 const QgsRectangle bbox = geom->boundingBox();
7126 const double width = bbox.width();
7127 const double height = bbox.height();
7128 const double size = width > height ? width : height;
7129 const double tolerance = size / 100.0;
7130 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
7131 testResult = radiusValue >= minInscribedCircleRadius;
7132 radiusValues.append( radiusValues );
7133 }
7134 } // end for parts
7135
7136 // Get the max values
7137 if ( !radiusValues.isEmpty() )
7138 {
7139 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
7140 }
7141
7142 if ( ! overlapValues.isEmpty() )
7143 {
7144 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
7145 }
7146
7147 return testResult;
7148
7149 };
7150
7151
7152 bool found = false;
7153 int foundCount = 0;
7154 QVariantList results;
7155
7156 QListIterator<QgsFeature> i( features );
7157 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
7158 {
7159
7160 QgsFeature feat2 = i.next();
7161
7162
7163 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
7164 {
7165
7166 double overlapValue = -1;
7167 double radiusValue = -1;
7168
7169 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
7170 {
7171 const QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
7172
7173 // Depending on the intersection geometry type and on the geometry type of
7174 // the tested geometry we can run different tests and collect different measures
7175 // that can be used for sorting (if required).
7176 switch ( intersection.type() )
7177 {
7178
7179 case QgsWkbTypes::GeometryType::PolygonGeometry:
7180 {
7181
7182 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
7183 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
7184
7185 if ( ! testResult && overlapOrRadiusFilter )
7186 {
7187 continue;
7188 }
7189
7190 break;
7191 }
7192
7193 case QgsWkbTypes::GeometryType::LineGeometry:
7194 {
7195
7196 // If the intersection is a linestring and a minimum circle is required
7197 // we can discard this result immediately.
7198 if ( minInscribedCircleRadius != -1 )
7199 {
7200 continue;
7201 }
7202
7203 // Otherwise a test for the overlap value is performed.
7204 const bool testResult { testLinestring( intersection, overlapValue ) };
7205
7206 if ( ! testResult && overlapOrRadiusFilter )
7207 {
7208 continue;
7209 }
7210
7211 break;
7212 }
7213
7214 case QgsWkbTypes::GeometryType::PointGeometry:
7215 {
7216
7217 // If the intersection is a point and a minimum circle is required
7218 // we can discard this result immediately.
7219 if ( minInscribedCircleRadius != -1 )
7220 {
7221 continue;
7222 }
7223
7224 bool testResult { false };
7225 if ( minOverlap != -1 || requireMeasures )
7226 {
7227 // Initially set this to 0 because it's a point intersection...
7228 overlapValue = 0;
7229 // ... but if the target geometry is not a point and the source
7230 // geometry is a point, we must record the length or the area
7231 // of the intersected geometry and use that as a measure for
7232 // sorting or reporting.
7233 if ( geometry.type() == QgsWkbTypes::GeometryType::PointGeometry )
7234 {
7235 switch ( feat2.geometry().type() )
7236 {
7237 case QgsWkbTypes::GeometryType::UnknownGeometry:
7238 case QgsWkbTypes::GeometryType::NullGeometry:
7239 case QgsWkbTypes::GeometryType::PointGeometry:
7240 {
7241 break;
7242 }
7243 case QgsWkbTypes::GeometryType::LineGeometry:
7244 {
7245 testResult = testLinestring( feat2.geometry(), overlapValue );
7246 break;
7247 }
7248 case QgsWkbTypes::GeometryType::PolygonGeometry:
7249 {
7250 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
7251 break;
7252 }
7253 }
7254 }
7255
7256 if ( ! testResult && overlapOrRadiusFilter )
7257 {
7258 continue;
7259 }
7260
7261 }
7262 break;
7263 }
7264
7265 case QgsWkbTypes::GeometryType::NullGeometry:
7266 case QgsWkbTypes::GeometryType::UnknownGeometry:
7267 {
7268 continue;
7269 }
7270 }
7271 }
7272
7273 found = true;
7274 foundCount++;
7275
7276 // We just want a single boolean result if there is any intersect: finish and return true
7277 if ( testOnly )
7278 break;
7279
7280 if ( !invert )
7281 {
7282 // We want a list of attributes / geometries / other expression values, evaluate now
7283 subContext.setFeature( feat2 );
7284 const QVariant expResult = subExpression.evaluate( &subContext );
7285
7286 if ( requireMeasures )
7287 {
7288 QVariantMap resultRecord;
7289 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
7290 resultRecord.insert( QStringLiteral( "result" ), expResult );
7291 // Overlap is always added because return measures was set
7292 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
7293 // Radius is only added when is different than -1 (because for linestrings is not set)
7294 if ( radiusValue != -1 )
7295 {
7296 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
7297 }
7298 results.append( resultRecord );
7299 }
7300 else
7301 {
7302 results.append( expResult );
7303 }
7304 }
7305 else
7306 {
7307 // If not, results is a list of found ids, which we'll inverse and evaluate below
7308 results.append( feat2.id() );
7309 }
7310 }
7311 }
7312
7313 if ( testOnly )
7314 {
7315 if ( invert )
7316 found = !found;//for disjoint condition
7317 return found;
7318 }
7319
7320 if ( !invert )
7321 {
7322 if ( requireMeasures )
7323 {
7324 if ( sortByMeasure )
7325 {
7326 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
7327 {
7328 return sortAscending ?
7329 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
7330 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
7331 } );
7332 }
7333 // Resize
7334 if ( limit > 0 && results.size() > limit )
7335 {
7336 results.erase( results.begin() + limit );
7337 }
7338
7339 if ( ! returnDetails ) //#spellok
7340 {
7341 QVariantList expResults;
7342 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
7343 {
7344 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
7345 }
7346 return expResults;
7347 }
7348 }
7349
7350 return results;
7351 }
7352
7353 // for disjoint condition returns the results for cached layers not intersected feats
7354 QVariantList disjoint_results;
7355 QgsFeature feat2;
7356 QgsFeatureRequest request2;
7357 request2.setLimit( limit );
7358 if ( context )
7359 request2.setFeedback( context->feedback() );
7360 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
7361 while ( fi.nextFeature( feat2 ) )
7362 {
7363 if ( !results.contains( feat2.id() ) )
7364 {
7365 subContext.setFeature( feat2 );
7366 disjoint_results.append( subExpression.evaluate( &subContext ) );
7367 }
7368 }
7369 return disjoint_results;
7370
7371}
7372
7373// Intersect functions:
7374
7375static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7376{
7377 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
7378}
7379
7380static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7381{
7382 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
7383}
7384
7385static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7386{
7387 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
7388}
7389
7390static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7391{
7392 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
7393}
7394
7395static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7396{
7397 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
7398}
7399
7400static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7401{
7402 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
7403}
7404
7405static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7406{
7407 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
7408}
7409
7410static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7411{
7412 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
7413}
7414
7415const QList<QgsExpressionFunction *> &QgsExpression::Functions()
7416{
7417 // The construction of the list isn't thread-safe, and without the mutex,
7418 // crashes in the WFS provider may occur, since it can parse expressions
7419 // in parallel.
7420 // The mutex needs to be recursive.
7421 static QRecursiveMutex sFunctionsMutex;
7422 QMutexLocker locker( &sFunctionsMutex );
7423
7424 QList<QgsExpressionFunction *> &functions = *sFunctions();
7425
7426 if ( functions.isEmpty() )
7427 {
7429 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
7430 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
7431 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
7432
7433 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
7434 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7435 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7436
7437 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
7438 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
7439
7440 functions
7441 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
7442 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
7443 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
7444 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
7445 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
7446 << new QgsStaticExpressionFunction( QStringLiteral( "project" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "elevation" ), true, M_PI_2 ), fcnProject, QStringLiteral( "GeometryGroup" ) )
7447 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
7448 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
7449 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
7450 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
7451 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
7452 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
7453 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
7454 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
7455 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
7456 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
7457 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
7458 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
7459 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
7460
7461 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
7462 randFunc->setIsStatic( false );
7463 functions << randFunc;
7464
7465 QgsStaticExpressionFunction *randfFunc = new QgsStaticExpressionFunction( QStringLiteral( "randf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ), true, 0.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ), true, 1.0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRndF, QStringLiteral( "Math" ) );
7466 randfFunc->setIsStatic( false );
7467 functions << randfFunc;
7468
7469 functions
7470 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7471 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
7472 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
7473 << new QgsStaticExpressionFunction( QStringLiteral( "scale_linear" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ), fcnLinearScale, QStringLiteral( "Math" ) )
7474 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "domain_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "range_max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "exponent" ) ), fcnExpScale, QStringLiteral( "Math" ) )
7475 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
7476 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
7477 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
7478 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
7479 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
7480 << new QgsStaticExpressionFunction( QStringLiteral( "to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToString, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tostring" ) )
7481 << new QgsStaticExpressionFunction( QStringLiteral( "to_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDateTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todatetime" ) )
7482 << new QgsStaticExpressionFunction( QStringLiteral( "to_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToDate, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todate" ) )
7483 << new QgsStaticExpressionFunction( QStringLiteral( "to_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnToTime, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "totime" ) )
7484 << new QgsStaticExpressionFunction( QStringLiteral( "to_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInterval, QStringList() << QStringLiteral( "Conversions" ) << QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tointerval" ) )
7485 << new QgsStaticExpressionFunction( QStringLiteral( "to_dm" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinute, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todm" ) )
7486 << new QgsStaticExpressionFunction( QStringLiteral( "to_dms" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "axis" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "formatting" ), true ), fcnToDegreeMinuteSecond, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todms" ) )
7487 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
7488 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7489 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
7490 << new QgsStaticExpressionFunction( QStringLiteral( "if" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "condition" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_true" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "result_when_false" ) ), fcnIf, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
7491 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
7492
7493 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
7495 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7496 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
7497 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
7498 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7499 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7500 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
7501 fcnAggregate,
7502 QStringLiteral( "Aggregates" ),
7503 QString(),
7504 []( const QgsExpressionNodeFunction * node )
7505 {
7506 // usesGeometry callback: return true if @parent variable is referenced
7507
7508 if ( !node )
7509 return true;
7510
7511 if ( !node->args() )
7512 return false;
7513
7514 QSet<QString> referencedVars;
7515 if ( node->args()->count() > 2 )
7516 {
7517 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
7518 referencedVars = subExpressionNode->referencedVariables();
7519 }
7520
7521 if ( node->args()->count() > 3 )
7522 {
7523 QgsExpressionNode *filterNode = node->args()->at( 3 );
7524 referencedVars.unite( filterNode->referencedVariables() );
7525 }
7526 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
7527 },
7528 []( const QgsExpressionNodeFunction * node )
7529 {
7530 // referencedColumns callback: return AllAttributes if @parent variable is referenced
7531
7532 if ( !node )
7533 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
7534
7535 if ( !node->args() )
7536 return QSet<QString>();
7537
7538 QSet<QString> referencedCols;
7539 QSet<QString> referencedVars;
7540
7541 if ( node->args()->count() > 2 )
7542 {
7543 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
7544 referencedVars = subExpressionNode->referencedVariables();
7545 referencedCols = subExpressionNode->referencedColumns();
7546 }
7547 if ( node->args()->count() > 3 )
7548 {
7549 QgsExpressionNode *filterNode = node->args()->at( 3 );
7550 referencedVars = filterNode->referencedVariables();
7551 referencedCols.unite( filterNode->referencedColumns() );
7552 }
7553
7554 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
7555 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
7556 else
7557 return referencedCols;
7558 },
7559 true
7560 )
7561
7562 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
7563 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
7564 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
7565 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
7566 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
7567 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
7568 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
7569
7570 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7571 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7572 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7573 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7574 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7575 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7576 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7577 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7578 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7579 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7580 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7581 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7582 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7583 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7584 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7585 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7586 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7587 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7588 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7589 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7590 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
7591
7592 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
7593 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
7594
7595 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
7596 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
7597 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
7598 fcnAge, QStringLiteral( "Date and Time" ) )
7599 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
7600 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
7601 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
7602 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
7603 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
7604 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
7605 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
7606 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
7607 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
7608 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
7609 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
7610 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
7611 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
7612 fcnMakeDate, QStringLiteral( "Date and Time" ) )
7613 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
7614 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
7615 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
7616 fcnMakeTime, QStringLiteral( "Date and Time" ) )
7617 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
7618 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
7619 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
7620 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
7621 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
7622 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
7623 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
7624 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
7625 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
7626 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
7627 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
7628 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
7629 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
7630 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
7631 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
7632 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
7633 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
7634 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
7635 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
7636 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
7637 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
7638 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
7639 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
7640 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
7641 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
7642 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
7643 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
7644 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
7645 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
7646 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
7647 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
7648 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
7649 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
7650 false, QSet< QString >(), false, QStringList(), true )
7651 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
7652 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
7653 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
7654 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
7655 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
7656 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
7657 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
7658 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatNumber, QStringLiteral( "String" ) )
7659 << new QgsStaticExpressionFunction( QStringLiteral( "format_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "format" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() ), fcnFormatDate, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "Date and Time" ) )
7660 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
7661 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
7662 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
7663 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
7664 fcnColorMixRgb, QStringLiteral( "Color" ) )
7665 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
7666 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
7667 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
7668 fcnColorRgb, QStringLiteral( "Color" ) )
7669 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
7670 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
7671 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
7672 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7673 fncColorRgba, QStringLiteral( "Color" ) )
7674 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
7675 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7676 fcnRampColor, QStringLiteral( "Color" ) )
7677 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
7678 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
7679 fcnCreateRamp, QStringLiteral( "Color" ) )
7680 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7681 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7682 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
7683 fcnColorHsl, QStringLiteral( "Color" ) )
7684 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7685 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7686 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
7687 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7688 fncColorHsla, QStringLiteral( "Color" ) )
7689 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7690 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7691 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7692 fcnColorHsv, QStringLiteral( "Color" ) )
7693 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
7694 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
7695 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
7696 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7697 fncColorHsva, QStringLiteral( "Color" ) )
7698 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
7699 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
7700 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
7701 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
7702 fcnColorCmyk, QStringLiteral( "Color" ) )
7703 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
7704 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
7705 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
7706 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
7707 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
7708 fncColorCmyka, QStringLiteral( "Color" ) )
7709 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7710 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
7711 fncColorPart, QStringLiteral( "Color" ) )
7712 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7713 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
7714 fncDarker, QStringLiteral( "Color" ) )
7715 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
7716 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
7717 fncLighter, QStringLiteral( "Color" ) )
7718 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
7719
7720 // file info
7721 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7722 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
7723 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7724 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
7725 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7726 fcnFileExists, QStringLiteral( "Files and Paths" ) )
7727 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7728 fcnFileName, QStringLiteral( "Files and Paths" ) )
7729 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7730 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
7731 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7732 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
7733 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7734 fcnFilePath, QStringLiteral( "Files and Paths" ) )
7735 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7736 fcnFileSize, QStringLiteral( "Files and Paths" ) )
7737
7738 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
7739 fcnExif, QStringLiteral( "Files and Paths" ) )
7740 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
7741 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
7742
7743 // hash
7744 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
7745 fcnGenericHash, QStringLiteral( "Conversions" ) )
7746 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7747 fcnHashMd5, QStringLiteral( "Conversions" ) )
7748 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7749 fcnHashSha256, QStringLiteral( "Conversions" ) )
7750
7751 //base64
7752 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
7753 fcnToBase64, QStringLiteral( "Conversions" ) )
7754 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
7755 fcnFromBase64, QStringLiteral( "Conversions" ) )
7756
7757 // deprecated stuff - hidden from users
7758 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
7759
7760 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
7761 geomFunc->setIsStatic( false );
7762 functions << geomFunc;
7763
7764 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
7765 areaFunc->setIsStatic( false );
7766 functions << areaFunc;
7767
7768 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
7769
7770 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
7771 lengthFunc->setIsStatic( false );
7772 functions << lengthFunc;
7773
7774 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
7775 perimeterFunc->setIsStatic( false );
7776 functions << perimeterFunc;
7777
7778 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
7779
7780 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
7782 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
7783
7784 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
7785 xFunc->setIsStatic( false );
7786 functions << xFunc;
7787
7788 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
7789 yFunc->setIsStatic( false );
7790 functions << yFunc;
7791
7792 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
7793 zFunc->setIsStatic( false );
7794 functions << zFunc;
7795
7796 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
7797 {
7798 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
7799 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
7800 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
7801 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
7802 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
7803 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
7804 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
7805 };
7806 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
7807 while ( i.hasNext() )
7808 {
7809 i.next();
7811 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7812 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
7813 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7814 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
7815 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
7816 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
7817 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
7818 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
7819 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
7820 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
7821
7822 // The current feature is accessed for the geometry, so this should not be cached
7823 fcnGeomOverlayFunc->setIsStatic( false );
7824 functions << fcnGeomOverlayFunc;
7825 }
7826
7827 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
7828 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
7829 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
7830 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
7831 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
7832 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
7833 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
7834 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
7835 // The current feature is accessed for the geometry, so this should not be cached
7836 fcnGeomOverlayNearestFunc->setIsStatic( false );
7837 functions << fcnGeomOverlayNearestFunc;
7838
7839 functions
7840 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
7841 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
7842 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
7843 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
7844 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
7845 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
7846 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
7847 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
7848 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7849 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
7850 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
7851 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
7852 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
7853 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
7854 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
7855 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
7856 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
7857 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
7858 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
7859 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
7860 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7861 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
7862 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
7863 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
7864 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
7865 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7866 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
7867 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
7868 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
7869 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
7870 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7871 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
7872 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
7873 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
7874 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
7875 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
7876 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
7877 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7878 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
7879 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
7880 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
7881 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
7882 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
7883 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7884 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
7885 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
7886 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
7887 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
7888 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
7889 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
7890 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
7891 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
7892 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
7893 {
7894 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7895#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
7896 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
7897#else
7898 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
7899#endif
7900 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
7901 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
7902 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) );
7903 xAtFunc->setIsStatic( false );
7904 functions << xAtFunc;
7905
7906 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "i" ) ), fcnYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) << QStringLiteral( "y_at" ) );
7907 yAtFunc->setIsStatic( false );
7908 functions << yAtFunc;
7909
7910 functions
7911 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
7912 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
7913 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
7914 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
7915 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
7916 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
7917 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
7918 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
7919 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
7920 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
7921 << new QgsStaticExpressionFunction( QStringLiteral( "intersects_bbox" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ), fcnBbox, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "bbox" ) )
7922 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7923 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7924 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
7925 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7926 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7927 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
7928 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7929 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7930 fcnTouches, QStringLiteral( "GeometryGroup" ) )
7931 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7932 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7933 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
7934 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7935 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7936 fcnContains, QStringLiteral( "GeometryGroup" ) )
7937 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7938 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7939 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
7940 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
7941 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
7942 fcnWithin, QStringLiteral( "GeometryGroup" ) )
7943 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7944 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
7945 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
7946 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
7947 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7948 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
7949 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
7950 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
7951 fcnRotate, QStringLiteral( "GeometryGroup" ) )
7952 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7953 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
7954 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
7955 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
7956 fcnScale, QStringLiteral( "GeometryGroup" ) )
7957 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7958 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
7959 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
7960 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
7961 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
7962 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
7963 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
7964 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
7965 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
7966 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
7967 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
7968 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7969 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7970 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
7971 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
7972 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
7973 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
7974 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
7975 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7976 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
7977 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7978 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
7979 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
7980 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
7981 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
7982 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
7983 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
7984 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
7985 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
7986 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7987 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
7988 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
7989 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7990 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
7991 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7992 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7993 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
7994 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
7995 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
7996 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
7997 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
7998 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
7999 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
8000 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8001 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8002 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
8003 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
8004 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
8005 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
8006 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8007 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
8008 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
8009 fcnExtend, QStringLiteral( "GeometryGroup" ) )
8010 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
8011 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
8012 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8013 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
8014 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, QStringLiteral( "GeometryGroup" ) )
8015 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
8016 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8017 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8018 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
8019 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8020 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
8021 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
8022 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
8023 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
8024 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
8025 {
8026 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
8027 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8028 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
8029 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
8030 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
8031 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
8032 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
8033 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
8034 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
8035 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
8036 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
8037 {
8038 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8039 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8040 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8041 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8042 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
8043 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
8044 {
8045 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8046 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8047 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8048 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8049 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8050 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8051 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8052 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
8053 {
8054 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8055 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8056 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8057 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8058 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
8059 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
8060 {
8061 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8062 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8063 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8064 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8065 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8066 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8067 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8068 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
8069 {
8070 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8071 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
8072 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
8073 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
8074 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
8075 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
8076 {
8077 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8078 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
8079 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
8080 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
8081 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
8082 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
8083 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
8084 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
8085 {
8086 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8087 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
8088 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
8089 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
8090 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
8091 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
8092 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
8093 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
8094 {
8095 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8096 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
8097 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
8098 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
8099 {
8100 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8101 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
8102 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
8103 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
8104 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
8105 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
8106 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
8107 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
8108 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
8109 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
8110 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
8111 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
8112 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8113 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
8114 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8115 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
8116 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
8117 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
8118 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8119 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
8120 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
8121 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8122 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
8123 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
8124 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8125 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
8126 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
8127 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8128 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8129 fcnDifference, QStringLiteral( "GeometryGroup" ) )
8130 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8131 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8132 fcnDistance, QStringLiteral( "GeometryGroup" ) )
8133 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
8134 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
8135 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
8136 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8137 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8138 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
8139 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8140 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8141 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
8142 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8143 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8144 fcnCombine, QStringLiteral( "GeometryGroup" ) )
8145 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8146 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8147 fcnCombine, QStringLiteral( "GeometryGroup" ) )
8148 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8149 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
8150 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
8151 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8152 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
8153 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
8154 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8155 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
8156 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
8157 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
8158 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8159 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
8160 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
8161 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
8162 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8163 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
8164 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8165 fcnZMax, QStringLiteral( "GeometryGroup" ) )
8166 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8167 fcnZMin, QStringLiteral( "GeometryGroup" ) )
8168 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8169 fcnMMax, QStringLiteral( "GeometryGroup" ) )
8170 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8171 fcnMMin, QStringLiteral( "GeometryGroup" ) )
8172 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8173 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
8174 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
8175 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
8176
8177
8178 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8179 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
8180 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
8181 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
8182
8183 orderPartsFunc->setIsStaticFunction(
8184 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8185 {
8186 const QList< QgsExpressionNode *> argList = node->args()->list();
8187 for ( QgsExpressionNode *argNode : argList )
8188 {
8189 if ( !argNode->isStatic( parent, context ) )
8190 return false;
8191 }
8192
8193 if ( node->args()->count() > 1 )
8194 {
8195 QgsExpressionNode *argNode = node->args()->at( 1 );
8196
8197 QString expString = argNode->eval( parent, context ).toString();
8198
8199 QgsExpression e( expString );
8200
8201 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
8202 return true;
8203 }
8204
8205 return true;
8206 } );
8207
8208 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8209 {
8210 if ( node->args()->count() > 1 )
8211 {
8212 QgsExpressionNode *argNode = node->args()->at( 1 );
8213 QString expression = argNode->eval( parent, context ).toString();
8214 QgsExpression e( expression );
8215 e.prepare( context );
8216 context->setCachedValue( expression, QVariant::fromValue( e ) );
8217 }
8218 return true;
8219 }
8220 );
8221 functions << orderPartsFunc;
8222
8223 functions
8224 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8225 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8226 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
8227 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
8228 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
8229 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
8230 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8231 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
8232 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8233 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
8234 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8235 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
8236 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8237 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
8238 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8239 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
8240 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
8241 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
8242
8243
8244 // **Record** functions
8245
8246 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
8247 idFunc->setIsStatic( false );
8248 functions << idFunc;
8249
8250 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
8251 currentFeatureFunc->setIsStatic( false );
8252 functions << currentFeatureFunc;
8253
8254 QgsStaticExpressionFunction *uuidFunc = new QgsStaticExpressionFunction( QStringLiteral( "uuid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "format" ), true, QStringLiteral( "WithBraces" ) ), fcnUuid, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$uuid" ) );
8255 uuidFunc->setIsStatic( false );
8256 functions << uuidFunc;
8257
8258 functions
8259 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8260 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
8261 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
8262 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
8263 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8264 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
8265 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
8266
8267 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
8268 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8269 attributesFunc->setIsStatic( false );
8270 functions << attributesFunc;
8271 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
8272 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8273 representAttributesFunc->setIsStatic( false );
8274 functions << representAttributesFunc;
8275
8277 QStringLiteral( "maptip" ),
8278 -1,
8279 fcnFeatureMaptip,
8280 QStringLiteral( "Record and Attributes" ),
8281 QString(),
8282 false,
8283 QSet<QString>()
8284 );
8285 maptipFunc->setIsStatic( false );
8286 functions << maptipFunc;
8287
8289 QStringLiteral( "display_expression" ),
8290 -1,
8291 fcnFeatureDisplayExpression,
8292 QStringLiteral( "Record and Attributes" ),
8293 QString(),
8294 false,
8295 QSet<QString>()
8296 );
8297 displayFunc->setIsStatic( false );
8298 functions << displayFunc;
8299
8301 QStringLiteral( "is_selected" ),
8302 -1,
8303 fcnIsSelected,
8304 QStringLiteral( "Record and Attributes" ),
8305 QString(),
8306 false,
8307 QSet<QString>()
8308 );
8309 isSelectedFunc->setIsStatic( false );
8310 functions << isSelectedFunc;
8311
8312 functions
8314 QStringLiteral( "num_selected" ),
8315 -1,
8316 fcnNumSelected,
8317 QStringLiteral( "Record and Attributes" ),
8318 QString(),
8319 false,
8320 QSet<QString>()
8321 );
8322
8323 functions
8325 QStringLiteral( "sqlite_fetch_and_increment" ),
8327 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
8328 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
8329 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
8330 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
8331 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
8332 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
8333 fcnSqliteFetchAndIncrement,
8334 QStringLiteral( "Record and Attributes" )
8335 );
8336
8337 // **Fields and Values** functions
8338 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
8339
8340 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8341 {
8342 Q_UNUSED( context )
8343 if ( node->args()->count() == 1 )
8344 {
8345 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
8346 if ( colRef )
8347 {
8348 return true;
8349 }
8350 else
8351 {
8352 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
8353 return false;
8354 }
8355 }
8356 else if ( node->args()->count() == 2 )
8357 {
8358 return true;
8359 }
8360 else
8361 {
8362 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
8363 return false;
8364 }
8365 }
8366 );
8367
8368 functions << representValueFunc;
8369
8370 // **General** functions
8371 functions
8372 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8373 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
8374 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
8375 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
8377 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8378 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
8379 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
8380 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
8382 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
8383 fcnMimeType, QStringLiteral( "General" ) )
8384 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8385 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
8386 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
8387
8388 // **var** function
8389 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
8390 varFunction->setIsStaticFunction(
8391 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8392 {
8393 /* A variable node is static if it has a static name and the name can be found at prepare
8394 * time and is tagged with isStatic.
8395 * It is not static if a variable is set during iteration or not tagged isStatic.
8396 * (e.g. geom_part variable)
8397 */
8398 if ( node->args()->count() > 0 )
8399 {
8400 QgsExpressionNode *argNode = node->args()->at( 0 );
8401
8402 if ( !argNode->isStatic( parent, context ) )
8403 return false;
8404
8405 const QString varName = argNode->eval( parent, context ).toString();
8406 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
8407 return false;
8408
8409 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
8410 return scope ? scope->isStatic( varName ) : false;
8411 }
8412 return false;
8413 }
8414 );
8415
8416 functions
8417 << varFunction;
8418
8419 functions << new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true );
8420
8421 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8422 evalFunc->setIsStaticFunction(
8423 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8424 {
8425 if ( node->args()->count() > 0 )
8426 {
8427 QgsExpressionNode *argNode = node->args()->at( 0 );
8428
8429 if ( argNode->isStatic( parent, context ) )
8430 {
8431 QString expString = argNode->eval( parent, context ).toString();
8432
8433 QgsExpression e( expString );
8434
8435 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
8436 return true;
8437 }
8438 }
8439
8440 return false;
8441 } );
8442
8443 functions << evalFunc;
8444
8445 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
8446 attributeFunc->setIsStaticFunction(
8447 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
8448 {
8449 const QList< QgsExpressionNode *> argList = node->args()->list();
8450 for ( QgsExpressionNode *argNode : argList )
8451 {
8452 if ( !argNode->isStatic( parent, context ) )
8453 return false;
8454 }
8455
8456 if ( node->args()->count() == 1 )
8457 {
8458 // not static -- this is the variant which uses the current feature taken direct from the expression context
8459 return false;
8460 }
8461
8462 return true;
8463 } );
8464 functions << attributeFunc;
8465
8466 functions
8467 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
8469 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
8470
8471 // functions for arrays
8474 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8475 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
8476 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
8477 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
8478 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
8479 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
8480 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
8481 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
8482 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
8483 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
8484 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
8485 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
8486 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
8487 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
8488 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
8489 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
8490 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
8491 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
8492 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
8493 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
8494 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
8495 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ) )
8496 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
8497 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
8498 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
8499 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
8500 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
8501 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
8502 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
8503 << new QgsStaticExpressionFunction( QStringLiteral( "array_to_string" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnArrayToString, QStringLiteral( "Arrays" ) )
8504 << new QgsStaticExpressionFunction( QStringLiteral( "string_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "," ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnStringToArray, QStringLiteral( "Arrays" ) )
8505 << new QgsStaticExpressionFunction( QStringLiteral( "generate_series" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "stop" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "step" ), true, 1.0 ), fcnGenerateSeries, QStringLiteral( "Arrays" ) )
8506 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
8507
8508 //functions for maps
8509 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
8510 << new QgsStaticExpressionFunction( QStringLiteral( "to_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "json_string" ) ), fcnWriteJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "map_to_json" ) )
8511 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
8512 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
8513 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
8514 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
8515 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
8516 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
8517 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
8518 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
8519 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
8520 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
8521 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8522 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
8523 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
8524 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
8525 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
8526
8527 ;
8528
8530
8531 //QgsExpression has ownership of all built-in functions
8532 for ( QgsExpressionFunction *func : std::as_const( functions ) )
8533 {
8534 *sOwnedFunctions() << func;
8535 *sBuiltinFunctions() << func->name();
8536 sBuiltinFunctions()->append( func->aliases() );
8537 }
8538 }
8539 return functions;
8540}
8541
8542bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
8543{
8544 int fnIdx = functionIndex( function->name() );
8545 if ( fnIdx != -1 )
8546 {
8547 return false;
8548 }
8549 sFunctions()->append( function );
8550 if ( transferOwnership )
8551 sOwnedFunctions()->append( function );
8552 return true;
8553}
8554
8555bool QgsExpression::unregisterFunction( const QString &name )
8556{
8557 // You can never override the built in functions.
8558 if ( QgsExpression::BuiltinFunctions().contains( name ) )
8559 {
8560 return false;
8561 }
8562 int fnIdx = functionIndex( name );
8563 if ( fnIdx != -1 )
8564 {
8565 sFunctions()->removeAt( fnIdx );
8566 return true;
8567 }
8568 return false;
8569}
8570
8572{
8573 qDeleteAll( *sOwnedFunctions() );
8574 sOwnedFunctions()->clear();
8575}
8576
8578{
8579 if ( sBuiltinFunctions()->isEmpty() )
8580 {
8581 Functions(); // this method builds the gmBuiltinFunctions as well
8582 }
8583 return *sBuiltinFunctions();
8584}
8585
8586
8588 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
8589 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
8590 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
8591 QStringLiteral( "Arrays" ) )
8592{
8593
8594}
8595
8597{
8598 bool isStatic = false;
8599
8600 QgsExpressionNode::NodeList *args = node->args();
8601
8602 if ( args->count() < 2 )
8603 return false;
8604
8605 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8606 {
8607 isStatic = true;
8608 }
8609 return isStatic;
8610}
8611
8613{
8614 Q_UNUSED( node )
8615 QVariantList result;
8616
8617 if ( args->count() < 2 )
8618 // error
8619 return result;
8620
8621 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
8622
8623 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
8624 std::unique_ptr< QgsExpressionContext > tempContext;
8625 if ( !subContext )
8626 {
8627 tempContext = std::make_unique< QgsExpressionContext >();
8628 subContext = tempContext.get();
8629 }
8630
8632 subContext->appendScope( subScope );
8633
8634 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
8635 {
8636 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
8637 result << args->at( 1 )->eval( parent, subContext );
8638 }
8639
8640 if ( context )
8641 delete subContext->popScope();
8642
8643 return result;
8644}
8645
8646QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8647{
8648 // This is a dummy function, all the real handling is in run
8649 Q_UNUSED( values )
8650 Q_UNUSED( context )
8651 Q_UNUSED( parent )
8652 Q_UNUSED( node )
8653
8654 Q_ASSERT( false );
8655 return QVariant();
8656}
8657
8659{
8660 QgsExpressionNode::NodeList *args = node->args();
8661
8662 if ( args->count() < 2 )
8663 // error
8664 return false;
8665
8666 args->at( 0 )->prepare( parent, context );
8667
8668 QgsExpressionContext subContext;
8669 if ( context )
8670 subContext = *context;
8671
8673 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
8674 subContext.appendScope( subScope );
8675
8676 args->at( 1 )->prepare( parent, &subContext );
8677
8678 return true;
8679}
8680
8682 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
8683 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
8684 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8685 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
8686 QStringLiteral( "Arrays" ) )
8687{
8688
8689}
8690
8692{
8693 bool isStatic = false;
8694
8695 QgsExpressionNode::NodeList *args = node->args();
8696
8697 if ( args->count() < 2 )
8698 return false;
8699
8700 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8701 {
8702 isStatic = true;
8703 }
8704 return isStatic;
8705}
8706
8708{
8709 Q_UNUSED( node )
8710 QVariantList result;
8711
8712 if ( args->count() < 2 )
8713 // error
8714 return result;
8715
8716 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
8717
8718 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
8719 std::unique_ptr< QgsExpressionContext > tempContext;
8720 if ( !subContext )
8721 {
8722 tempContext = std::make_unique< QgsExpressionContext >();
8723 subContext = tempContext.get();
8724 }
8725
8727 subContext->appendScope( subScope );
8728
8729 int limit = 0;
8730 if ( args->count() >= 3 )
8731 {
8732 const QVariant limitVar = args->at( 2 )->eval( parent, context );
8733
8734 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
8735 {
8736 limit = limitVar.toInt();
8737 }
8738 else
8739 {
8740 return result;
8741 }
8742 }
8743
8744 for ( const QVariant &value : array )
8745 {
8746 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
8747 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
8748 {
8749 result << value;
8750
8751 if ( limit > 0 && limit == result.size() )
8752 break;
8753 }
8754 }
8755
8756 if ( context )
8757 delete subContext->popScope();
8758
8759 return result;
8760}
8761
8762QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8763{
8764 // This is a dummy function, all the real handling is in run
8765 Q_UNUSED( values )
8766 Q_UNUSED( context )
8767 Q_UNUSED( parent )
8768 Q_UNUSED( node )
8769
8770 Q_ASSERT( false );
8771 return QVariant();
8772}
8773
8775{
8776 QgsExpressionNode::NodeList *args = node->args();
8777
8778 if ( args->count() < 2 )
8779 // error
8780 return false;
8781
8782 args->at( 0 )->prepare( parent, context );
8783
8784 QgsExpressionContext subContext;
8785 if ( context )
8786 subContext = *context;
8787
8789 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
8790 subContext.appendScope( subScope );
8791
8792 args->at( 1 )->prepare( parent, &subContext );
8793
8794 return true;
8795}
8797 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
8798 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
8799 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8800 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
8801 QStringLiteral( "General" ) )
8802{
8803
8804}
8805
8807{
8808 bool isStatic = false;
8809
8810 QgsExpressionNode::NodeList *args = node->args();
8811
8812 if ( args->count() < 3 )
8813 return false;
8814
8815 // We only need to check if the node evaluation is static, if both - name and value - are static.
8816 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
8817 {
8818 QVariant name = args->at( 0 )->eval( parent, context );
8819 QVariant value = args->at( 1 )->eval( parent, context );
8820
8821 // Temporarily append a new scope to provide the variable
8822 appendTemporaryVariable( context, name.toString(), value );
8823 if ( args->at( 2 )->isStatic( parent, context ) )
8824 isStatic = true;
8825 popTemporaryVariable( context );
8826 }
8827
8828 return isStatic;
8829}
8830
8832{
8833 Q_UNUSED( node )
8834 QVariant result;
8835
8836 if ( args->count() < 3 )
8837 // error
8838 return result;
8839
8840 QVariant name = args->at( 0 )->eval( parent, context );
8841 QVariant value = args->at( 1 )->eval( parent, context );
8842
8843 const QgsExpressionContext *updatedContext = context;
8844 std::unique_ptr< QgsExpressionContext > tempContext;
8845 if ( !updatedContext )
8846 {
8847 tempContext = std::make_unique< QgsExpressionContext >();
8848 updatedContext = tempContext.get();
8849 }
8850
8851 appendTemporaryVariable( updatedContext, name.toString(), value );
8852 result = args->at( 2 )->eval( parent, updatedContext );
8853
8854 if ( context )
8855 popTemporaryVariable( updatedContext );
8856
8857 return result;
8858}
8859
8860QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
8861{
8862 // This is a dummy function, all the real handling is in run
8863 Q_UNUSED( values )
8864 Q_UNUSED( context )
8865 Q_UNUSED( parent )
8866 Q_UNUSED( node )
8867
8868 Q_ASSERT( false );
8869 return QVariant();
8870}
8871
8873{
8874 QgsExpressionNode::NodeList *args = node->args();
8875
8876 if ( args->count() < 3 )
8877 // error
8878 return false;
8879
8880 QVariant name = args->at( 0 )->prepare( parent, context );
8881 QVariant value = args->at( 1 )->prepare( parent, context );
8882
8883 const QgsExpressionContext *updatedContext = context;
8884 std::unique_ptr< QgsExpressionContext > tempContext;
8885 if ( !updatedContext )
8886 {
8887 tempContext = std::make_unique< QgsExpressionContext >();
8888 updatedContext = tempContext.get();
8889 }
8890
8891 appendTemporaryVariable( updatedContext, name.toString(), value );
8892 args->at( 2 )->prepare( parent, updatedContext );
8893
8894 if ( context )
8895 popTemporaryVariable( updatedContext );
8896
8897 return true;
8898}
8899
8900void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
8901{
8902 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
8903 delete updatedContext->popScope();
8904}
8905
8906void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
8907{
8910
8911 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
8912 updatedContext->appendScope( scope );
8913}
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition: qgis.h:1716
@ ScaleDashOnly
Only dash lengths are adjusted.
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
@ ScaleGapOnly
Only gap lengths are adjusted.
@ Success
Operation succeeded.
JoinStyle
Join styles for buffers.
Definition: qgis.h:1033
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:1020
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition: qgis.h:1701
@ HalfDash
Start or finish the pattern with a half length dash.
@ HalfGap
Start or finish the pattern with a half length gap.
@ FullGap
Start or finish the pattern with a full gap.
@ FullDash
Start or finish the pattern with a full dash.
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition: qgis.h:1046
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Abstract base class for all geometries.
virtual bool addZValue(double zValue=0)=0
Adds a z-dimension to the geometry, initialized to a preset value.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual bool addMValue(double mValue=0)=0
Adds a measure to the geometry, initialized to a preset value.
part_iterator parts_end()
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual QgsCoordinateSequence coordinateSequence() const =0
Retrieves the sequence of geometries, rings and nodes.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
part_iterator parts_begin()
Returns STL-style iterator pointing to the first part of the geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
QString abstract() const
Returns a free-form description of the resource.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
Aggregate
Available aggregates to calculate.
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
@ StringMaximumLength
Maximum length of string (string fields only)
@ ThirdQuartile
Third quartile (numeric fields only)
@ Range
Range of values (max - min) (numeric and datetime fields only)
@ ArrayAggregate
Create an array of values.
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only)
@ FirstQuartile
First quartile (numeric fields only)
@ Median
Median of values (numeric fields only)
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
@ CountMissing
Number of missing (null) values.
@ StDevSample
Sample standard deviation of values (numeric fields only)
@ Majority
Majority of values.
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
@ Mean
Mean of values (numeric fields only)
@ StringMinimumLength
Minimum length of string (string fields only)
@ CountDistinct
Number of distinct values.
@ Minority
Minority of values.
static Aggregate stringToAggregate(const QString &string, bool *ok=nullptr)
Converts a string to a aggregate type.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Handles the array_filter(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
Handles the array loopingarray_Foreach(array, expression) expression function.
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
Circle geometry type.
Definition: qgscircle.h:44
Abstract base class for color ramps.
Definition: qgscolorramp.h:30
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
Format
Available formats for displaying coordinates.
@ FormatDegreesMinutes
Degrees and decimal minutes, eg 30degrees 45.55'.
@ FormatDegreesMinutesSeconds
Degrees, minutes and seconds, eg 30 degrees 45'30".
static QString formatY(double y, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats a y coordinate value according to the specified parameters.
@ FlagDegreesUseStringSuffix
Include a direction suffix (eg 'N', 'E', 'S' or 'W'), otherwise a "-" prefix is used for west and sou...
@ FlagDegreesPadMinutesSeconds
Pad minute and second values with leading zeros, eg '05' instead of '5'.
static QString formatX(double x, Format format, int precision=12, FormatFlags flags=FlagDegreesUseStringSuffix)
Formats an x coordinate value according to the specified parameters.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Curve polygon geometry type.
int ringCount(int part=0) const override SIP_HOLDGIL
Returns the number of rings of which this geometry is built.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
double roundness() const
Returns the roundness of the curve polygon.
double area() const override SIP_HOLDGIL
Returns the planar, 2-dimensional area of the geometry.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition: qgscurve.cpp:277
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition: qgscurve.cpp:272
virtual QgsCurve * reversed() const =0
Returns a reversed copy of the curve, where the direction of the curve has been flipped.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
double measurePerimeter(const QgsGeometry &geometry) const
Measures the perimeter of a polygon geometry.
double measureLength(const QgsGeometry &geometry) const
Measures the length of a geometry.
double convertAreaMeasurement(double area, QgsUnitTypes::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
double convertLengthMeasurement(double length, QgsUnitTypes::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Ellipse geometry type.
Definition: qgsellipse.h:40
QString what() const
Definition: qgsexception.h:48
Contains utilities for working with EXIF tags in images.
Definition: qgsexiftools.h:33
static QgsPoint getGeoTag(const QString &imagePath, bool &ok)
Returns the geotagged coordinate stored in the image at imagePath.
static QVariant readTag(const QString &imagePath, const QString &key)
Returns the value of of an exif tag key stored in the image at imagePath.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
bool isStatic(const QString &name) const
Tests whether the variable with the specified name is static and can be cached.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static void registerContextFunctions()
Registers all known core functions provided by QgsExpressionContextScope objects.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
void setCachedValue(const QString &key, const QVariant &value) const
Sets a value to cache within the expression context.
QgsGeometry geometry() const
Convenience function for retrieving the geometry for the context, if set.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsExpressionContextScope * activeScopeForVariable(const QString &name)
Returns the currently active scope from the context for a specified variable name.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly by the expression to check if evaluation sh...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool hasGeometry() const
Returns true if the context has a geometry associated with it.
bool hasCachedValue(const QString &key) const
Returns true if the expression context contains a cached value with a matching key.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
QVariant cachedValue(const QString &key) const
Returns the matching cached value, if set.
bool hasFeature() const
Returns true if the context has a feature associated with it.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Represents a single parameter passed to a function.
A abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
Will be called during prepare to determine if the function is static.
virtual QStringList aliases() const
Returns a list of possible aliases for the function.
bool lazyEval() const
true if this function should use lazy evaluation.
static bool allParamsStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context)
This will return true if all the params for the provided function node are static within the constrai...
QString name() const
The name of the function.
virtual QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
Evaluates the function, first evaluating all required arguments before passing them to the function's...
virtual QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)=0
Returns result of evaluating the function.
virtual QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const
Returns a set of field names which are required for this function.
virtual bool handlesNull() const
Returns true if the function handles NULL values in arguments by itself, and the default NULL value h...
virtual bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const
This will be called during the prepare step() of an expression if it is not static.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes it value from a feature's field.
QString name() const
The name of the column.
An expression node for expression functions.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for literal values.
A list of expression nodes.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
QgsExpressionNode * at(int i)
Gets the node at position i in the list.
int count() const
Returns the number of nodes in the list.
Abstract base class for all nodes that can appear in an expression.
virtual QString dump() const =0
Dump this node into a serialized (part) of an expression.
QVariant eval(QgsExpression *parent, const QgsExpressionContext *context)
Evaluate this node with the given context and parent.
virtual bool isStatic(QgsExpression *parent, const QgsExpressionContext *context) const =0
Returns true if this node can be evaluated for a static value.
bool prepare(QgsExpression *parent, const QgsExpressionContext *context)
Prepare this node for evaluation.
virtual QSet< QString > referencedColumns() const =0
Abstract virtual method which returns a list of columns required to evaluate this node.
virtual QSet< QString > referencedVariables() const =0
Returns a set of all variables which are used in this expression.
A set of expression-related functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static const QList< QgsExpressionFunction * > & Functions()
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
static bool registerFunction(QgsExpressionFunction *function, bool transferOwnership=false)
Registers a function to the expression engine.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static int functionIndex(const QString &name)
Returns index of the function in Functions array.
static const QStringList & BuiltinFunctions()
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
QgsUnitTypes::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
static QString helpText(QString name)
Returns the help text for a specified function.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
QgsUnitTypes::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions)
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
QgsDistanceArea * geomCalculator()
Returns calculator used for distance and area calculations (used by $length, $area and $perimeter fun...
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
The OrderByClause class represents an order by clause for a QgsFeatureRequest.
Represents a list of OrderByClauses, with the most important first and the least important last.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setRequestMayBeNested(bool requestMayBeNested)
In case this request may be run nested within another already running iteration on the same connectio...
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setTimeout(int timeout)
Sets the timeout (in milliseconds) for the maximum time we should wait during feature requests before...
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setFeedback(QgsFeedback *feedback)
Attach a feedback object that can be queried regularly by the iterator to check if it should be cance...
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsVectorLayer * materialize(const QgsFeatureRequest &request, QgsFeedback *feedback=nullptr)
Materializes a request (query) made against this feature source, by running it over the source and re...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsFields fields
Definition: qgsfeature.h:66
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:219
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:338
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QString name
Definition: qgsfield.h:60
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:631
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
virtual bool removeGeometry(int nr)
Removes a geometry from the collection.
QgsGeometryCollection * createEmptyWithSameType() const override
Creates a new geometry with the same class and same WKB type as the original and transfers ownership.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
int partCount() const override
Returns count of parts contained in the geometry.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
static QVector< QgsLineString * > extractLineStrings(const QgsAbstractGeometry *geom)
Returns list of linestrings extracted from the passed geometry.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
double hausdorffDistanceDensify(const QgsGeometry &geom, double densifyFraction) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry offsetCurve(double distance, int segments, Qgis::JoinStyle joinStyle, double miterLimit) const
Returns an offset line at a given distance and side from an input line.
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry squareWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs square waves along the boundary of the geometry, with the specified wavelength and amplitu...
QgsGeometry triangularWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs triangular waves along the boundary of the geometry, with the specified wavelength and amp...
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const SIP_THROW(QgsNotSupportedException)
Returns a possibly concave polygon that contains all the points in the geometry.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
bool touches(const QgsGeometry &geometry) const
Returns true if the geometry touches another geometry.
QgsGeometry applyDashPattern(const QVector< double > &pattern, Qgis::DashPatternLineEndingRule startRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternLineEndingRule endRule=Qgis::DashPatternLineEndingRule::NoRule, Qgis::DashPatternSizeAdjustment adjustment=Qgis::DashPatternSizeAdjustment::ScaleBothDashAndGap, double patternOffset=0) const
Applies a dash pattern to a geometry, returning a MultiLineString geometry which is the input geometr...
QgsGeometry roundWaves(double wavelength, double amplitude, bool strictWavelength=false) const
Constructs rounded (sine-like) waves along the boundary of the geometry, with the specified wavelengt...
QgsGeometry nearestPoint(const QgsGeometry &other) const
Returns the nearest (closest) point on this geometry to another geometry.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsGeometry combine(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer for a (multi)linestring geometry, where the width at each node is ...
QgsMultiPointXY asMultiPoint() const
Returns the contents of the geometry as a multi-point.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
bool disjoint(const QgsGeometry &geometry) const
Returns true if the geometry is disjoint of another geometry.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsGeometry roundWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized rounded (sine-like) waves along the boundary of the geometry,...
double distance(const QgsGeometry &geom) const
Returns the minimum distance between this geometry and another geometry.
QgsGeometry interpolate(double distance) const
Returns an interpolated point on the geometry at the specified distance.
QgsGeometry extrude(double x, double y)
Returns an extruded version of this geometry.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
QgsGeometry singleSidedBuffer(double distance, int segments, Qgis::BufferSide side, Qgis::JoinStyle joinStyle=Qgis::JoinStyle::Round, double miterLimit=2.0) const
Returns a single sided buffer for a (multi)line geometry.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
bool isMultipart() const SIP_HOLDGIL
Returns true if WKB of the geometry is of WKBMulti* type.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
QgsGeometry forceRHR() const
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon is t...
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
bool equals(const QgsGeometry &geometry) const
Test if this geometry is exactly equal to another geometry.
bool isGeosValid(Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const
Checks validity of the geometry using GEOS.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:167
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a variable width buffer ("tapered buffer") for a (multi)curve geometry.
bool within(const QgsGeometry &geometry) const
Returns true if the geometry is completely within another geometry.
QgsGeometry orientedMinimumBoundingBox(double &area, double &angle, double &width, double &height) const
Returns the oriented minimum bounding box for the geometry, which is the smallest (by area) rotated r...
double area() const
Returns the planar, 2-dimensional area of the geometry.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool crosses(const QgsGeometry &geometry) const
Returns true if the geometry crosses another geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QString lastError() const SIP_HOLDGIL
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const SIP_THROW(QgsNotSupportedException)
Attempts to make an invalid geometry valid without losing vertices.
QgsGeometry convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry.
QgsGeometry sharedPaths(const QgsGeometry &other) const
Find paths shared between the two given lineal geometries (this and other).
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
QgsGeometry symDifference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
QgsGeometry minimalEnclosingCircle(QgsPointXY &center, double &radius, unsigned int segments=36) const
Returns the minimal enclosing circle for the geometry.
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
static QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
double distanceToVertex(int vertex) const
Returns the distance along this geometry from its first vertex to the specified vertex.
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsAbstractGeometry::vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
QgsGeometry forcePolygonClockwise() const
Forces geometries to respect the exterior ring is clockwise, interior rings are counter-clockwise con...
static QgsGeometry createWedgeBuffer(const QgsPoint &center, double azimuth, double angularWidth, double outerRadius, double innerRadius=0)
Creates a wedge shaped buffer from a center point.
QgsGeometry extendLine(double startDistance, double endDistance) const
Extends a (multi)line geometry by extrapolating out the start or end of the line by a specified dista...
QgsGeometry triangularWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized triangular waves along the boundary of the geometry, with the specified wavelen...
QgsGeometry squareWavesRandomized(double minimumWavelength, double maximumWavelength, double minimumAmplitude, double maximumAmplitude, unsigned long seed=0) const
Constructs randomized square waves along the boundary of the geometry, with the specified wavelength ...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Qgis::GeometryOperationResult translate(double dx, double dy, double dz=0.0, double dm=0.0)
Translates this geometry by dx, dy, dz and dm.
double interpolateAngle(double distance) const
Returns the angle parallel to the linestring or polygon boundary at the specified distance along the ...
double angleAtVertex(int vertex) const
Returns the bisector angle for this geometry at the specified vertex.
QgsGeometry smooth(unsigned int iterations=1, double offset=0.25, double minimumDistance=-1.0, double maxAngle=180.0) const
Smooths a geometry by rounding off corners using the Chaikin algorithm.
QgsGeometry forcePolygonCounterClockwise() const
Forces geometries to respect the exterior ring is counter-clockwise, interior rings are clockwise con...
QString asWkt(int precision=17) const
Exports the geometry to WKT.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QgsAbstractGeometry::vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool overlaps(const QgsGeometry &geometry) const
Returns true if the geometry overlaps another geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:99
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition: qgsgeos.cpp:2453
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
A representation of the interval between two datetime values.
Definition: qgsinterval.h:42
bool isValid() const
Returns true if the interval is valid.
Definition: qgsinterval.h:255
double days() const
Returns the interval duration in days.
double weeks() const
Returns the interval duration in weeks.
double months() const
Returns the interval duration in months (based on a 30 day month).
double seconds() const
Returns the interval duration in seconds.
Definition: qgsinterval.h:236
double years() const
Returns the interval duration in years (based on an average year length)
double hours() const
Returns the interval duration in hours.
double minutes() const
Returns the interval duration in minutes.
QStringList rights() const
Returns a list of attribution or copyright strings associated with the resource.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:78
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:326
virtual bool isEditable() const
Returns true if the layer can be edited.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:310
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:362
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:418
double minimumScale() const
Returns the minimum map scale (i.e.
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double maximumScale() const
Returns the maximum map scale (i.e.
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:342
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
@ SimplifyGeometry
The geometries can be simplified using the current map2pixel context state.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Multi line string geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Multi point geometry collection.
Definition: qgsmultipoint.h:30
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Custom exception class which is raised when an operation is not supported.
Definition: qgsexception.h:118
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override SIP_HOLDGIL
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgspoint.cpp:424
QgsPoint project(double distance, double azimuth, double inclination=90.0) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition: qgspoint.cpp:735
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
double inclination(const QgsPoint &other) const SIP_HOLDGIL
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition: qgspoint.cpp:723
QgsRelationManager * relationManager
Definition: qgsproject.h:114
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Quadrilateral geometry type.
static QgsQuadrilateral squareFromDiagonal(const QgsPoint &p1, const QgsPoint &p2) SIP_HOLDGIL
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
ConstructionOption
A quadrilateral can be constructed from 3 points where the second distance can be determined by the t...
@ Distance
Second distance is equal to the distance between 2nd and 3rd point.
@ Projected
Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segm...
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode) SIP_HOLDGIL
Construct a QgsQuadrilateral as a Rectangle from 3 points.
The RasterBandStats struct is a container for statistics about a single raster band.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double sum
The sum of all cells in the band. NO_DATA values are excluded.
double maximumValue
The maximum cell value in the raster band.
double range
The range is the distance between min & max.
virtual double sample(const QgsPointXY &point, int band, bool *ok=nullptr, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Samples a raster value from the specified band found at the point position.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:296
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
Regular Polygon geometry type.
ConstructionOption
A regular polygon can be constructed inscribed in a circle or circumscribed about a circle.
@ CircumscribedCircle
Circumscribed about a circle (the radius is the distance from the center to the midpoints of the side...
@ InscribedCircle
Inscribed in a circle (the radius is the distance between the center and vertices)
QgsPolygon * toPolygon() const
Returns as a polygon.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:47
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:46
bool isValid
Definition: qgsrelation.h:49
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > nearestNeighbor(const QgsPointXY &point, int neighbors=1, double maxDistance=0) const
Returns nearest neighbors to a point.
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
static QString quotedIdentifier(const QString &identifier)
Returns a properly quoted version of identifier.
static QString quotedValue(const QVariant &value)
Returns a properly quoted and escaped version of value for use in SQL strings.
c++ helper class for defining QgsExpression functions.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
void setIsStaticFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *) > &isStatic)
Set a function that will be called in the prepare step to determine if the function is static or not.
QStringList aliases() const override
Returns a list of possible aliases for the function.
void setPrepareFunction(const std::function< bool(const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext *)> &prepareFunc)
Set a function that will be called in the prepare step to determine if the function is static or not.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
void setIsStatic(bool isStatic)
Tag this function as either static or not static.
QgsStaticExpressionFunction(const QString &fnname, int params, FcnEval fcn, const QString &group, const QString &helpText=QString(), bool usesGeometry=false, const QSet< QString > &referencedColumns=QSet< QString >(), bool lazyEval=false, const QStringList &aliases=QStringList(), bool handlesNull=false)
Static function for evaluation against a QgsExpressionContext, using an unnamed list of parameter val...
QSet< QString > referencedColumns(const QgsExpressionNodeFunction *node) const override
Returns a set of field names which are required for this function.
bool usesGeometry(const QgsExpressionNodeFunction *node) const override
Does this function use a geometry object.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition: qgsstyle.cpp:468
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgssurface.h:43
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
static bool runOnMainThread(const Func &func, QgsFeedback *feedback=nullptr)
Guarantees that func is executed on the main thread.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
Triangle geometry type.
Definition: qgstriangle.h:34
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString mapTipTemplate
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QString displayExpression
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsEditorWidgetSetup editorWidgetSetup(int index) const
The editor widget setup defines which QgsFieldFormatter and editor widget will be used for the field ...
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QVariant aggregate(QgsAggregateCalculator::Aggregate aggregate, const QString &fieldOrExpression, const QgsAggregateCalculator::AggregateParameters &parameters=QgsAggregateCalculator::AggregateParameters(), QgsExpressionContext *context=nullptr, bool *ok=nullptr, QgsFeatureIds *fids=nullptr, QgsFeedback *feedback=nullptr, QString *error=nullptr) const
Calculates an aggregated value from the layer's features.
Handles the with_variable(name, value, node) expression function.
bool isStatic(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
Will be called during prepare to determine if the function is static.
QVariant run(QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Evaluates the function, first evaluating all required arguments before passing them to the function's...
QVariant func(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node) override
Returns result of evaluating the function.
bool prepare(const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context) const override
This will be called during the prepare step() of an expression if it is not static.
static QString geometryDisplayString(GeometryType type) SIP_HOLDGIL
Returns a display string for a geometry type.
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
int exec(const QString &sql, QString &errorMessage) const
Executes the sql command in the database.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
CORE_EXPORT QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
CORE_EXPORT QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
uint qHash(const QVariant &variant)
Hash for QVariant.
Definition: qgis.cpp:217
#define str(x)
Definition: qgis.cpp:37
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:119
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition: qgis.h:2581
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Q_DECLARE_METATYPE(QgsDatabaseQueryLogEntry)
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QList< QgsExpressionFunction * > ExpressionFunctionList
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction *)
bool(QgsGeometry::* RelationFunction)(const QgsGeometry &geometry) const
#define ENSURE_NO_EVAL_ERROR
#define FEAT_FROM_CONTEXT(c, f)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition: qgsgeometry.h:82
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2147
QLineF segment(int index, QRectF rect, double radius)
int precision
A bundle of parameters controlling aggregate calculation.
QString filter
Optional filter for calculating aggregate over a subset of features, or an empty string to use all fe...
QString delimiter
Delimiter to use for joining values with the StringConcatenate aggregate.
QgsFeatureRequest::OrderBy orderBy
Optional order by clauses.
Single variable definition for use within a QgsExpressionContextScope.
The Context struct stores the current layer and coordinate transform context.
Definition: qgsogcutils.h:60
const QgsMapLayer * layer
Definition: qgsogcutils.h:70
QgsCoordinateTransformContext transformContext
Definition: qgsogcutils.h:71
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31