QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsexpressionfunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsexpressionfunction.cpp
3 -------------------
4 begin : May 2017
5 copyright : (C) 2017 Matthias Kuhn
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18
19#include <random>
20#include <sqlite3.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgscolorrampimpl.h"
27#include "qgscoordinateutils.h"
28#include "qgscurve.h"
29#include "qgscurvepolygon.h"
30#include "qgsdistancearea.h"
31#include "qgsexception.h"
32#include "qgsexiftools.h"
36#include "qgsexpressionutils.h"
37#include "qgsfeaturerequest.h"
38#include "qgsfieldformatter.h"
40#include "qgsgeometryengine.h"
41#include "qgsgeometryutils.h"
42#include "qgsgeos.h"
43#include "qgshstoreutils.h"
44#include "qgslinestring.h"
46#include "qgsmessagelog.h"
47#include "qgsmultilinestring.h"
48#include "qgsmultipoint.h"
49#include "qgsogcutils.h"
50#include "qgspolygon.h"
51#include "qgsproviderregistry.h"
52#include "qgsquadrilateral.h"
53#include "qgsrasterbandstats.h"
54#include "qgsrasterlayer.h"
55#include "qgsregularpolygon.h"
56#include "qgsspatialindex.h"
57#include "qgsstringutils.h"
58#include "qgsstyle.h"
59#include "qgssymbollayerutils.h"
60#include "qgsthreadingutils.h"
61#include "qgstransaction.h"
62#include "qgstriangle.h"
63#include "qgsunittypes.h"
64#include "qgsvariantutils.h"
65#include "qgsvectorlayer.h"
67#include "qgsvectorlayerutils.h"
68
69#include <QCryptographicHash>
70#include <QMimeDatabase>
71#include <QProcessEnvironment>
72#include <QRegularExpression>
73#include <QUrlQuery>
74#include <QUuid>
75
76typedef QList<QgsExpressionFunction *> ExpressionFunctionList;
77
79Q_GLOBAL_STATIC( QStringList, sBuiltinFunctions )
81
84Q_DECLARE_METATYPE( std::shared_ptr<QgsVectorLayer> )
85
86const QString QgsExpressionFunction::helpText() const
87{
88 return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText;
89}
90
92{
93 Q_UNUSED( node )
94 // evaluate arguments
95 QVariantList argValues;
96 if ( args )
97 {
98 int arg = 0;
99 const QList< QgsExpressionNode * > argList = args->list();
100 argValues.reserve( argList.size() );
101 for ( QgsExpressionNode *n : argList )
102 {
103 QVariant v;
104 if ( lazyEval() )
105 {
106 // Pass in the node for the function to eval as it needs.
107 v = QVariant::fromValue( n );
108 }
109 else
110 {
111 v = n->eval( parent, context );
113 bool defaultParamIsNull = mParameterList.count() > arg && mParameterList.at( arg ).optional() && !mParameterList.at( arg ).defaultValue().isValid();
114 if ( QgsExpressionUtils::isNull( v ) && !defaultParamIsNull && !handlesNull() )
115 return QVariant(); // all "normal" functions return NULL, when any QgsExpressionFunction::Parameter is NULL (so coalesce is abnormal)
116 }
117 argValues.append( v );
118 arg++;
119 }
120 }
121
122 return func( argValues, context, parent, node );
123}
124
126{
127 Q_UNUSED( node )
128 return true;
129}
130
132{
133 return QStringList();
134}
135
137{
138 Q_UNUSED( parent )
139 Q_UNUSED( context )
140 Q_UNUSED( node )
141 return false;
142}
143
145{
146 Q_UNUSED( parent )
147 Q_UNUSED( context )
148 Q_UNUSED( node )
149 return true;
150}
151
153{
154 Q_UNUSED( node )
155 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
156}
157
159{
160 return mGroups.isEmpty() ? false : mGroups.contains( QStringLiteral( "deprecated" ) );
161}
162
164{
165 return ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 );
166}
167
169{
170 return mHandlesNull;
171}
172
173// doxygen doesn't like this constructor for some reason (maybe the function arguments?)
176 FcnEval fcn,
177 const QString &group,
178 const QString &helpText,
179 const std::function < bool ( const QgsExpressionNodeFunction *node ) > &usesGeometry,
180 const std::function < QSet<QString>( const QgsExpressionNodeFunction *node ) > &referencedColumns,
181 bool lazyEval,
182 const QStringList &aliases,
183 bool handlesNull )
184 : QgsExpressionFunction( fnname, params, group, helpText, lazyEval, handlesNull, false )
185 , mFnc( fcn )
186 , mAliases( aliases )
187 , mUsesGeometry( false )
188 , mUsesGeometryFunc( usesGeometry )
189 , mReferencedColumnsFunc( referencedColumns )
190{
191}
193
195{
196 return mAliases;
197}
198
200{
201 if ( mUsesGeometryFunc )
202 return mUsesGeometryFunc( node );
203 else
204 return mUsesGeometry;
205}
206
208{
209 mUsesGeometryFunc = usesGeometry;
210}
211
213{
214 if ( mReferencedColumnsFunc )
215 return mReferencedColumnsFunc( node );
216 else
217 return mReferencedColumns;
218}
219
221{
222 if ( mIsStaticFunc )
223 return mIsStaticFunc( node, parent, context );
224 else
225 return mIsStatic;
226}
227
229{
230 if ( mPrepareFunc )
231 return mPrepareFunc( node, parent, context );
232
233 return true;
234}
235
237{
238 mIsStaticFunc = isStatic;
239}
240
242{
243 mIsStaticFunc = nullptr;
244 mIsStatic = isStatic;
245}
246
247void QgsStaticExpressionFunction::setPrepareFunction( const std::function<bool ( const QgsExpressionNodeFunction *, QgsExpression *, const QgsExpressionContext * )> &prepareFunc )
248{
249 mPrepareFunc = prepareFunc;
250}
251
253{
254 if ( node && node->args() )
255 {
256 const QList< QgsExpressionNode * > argList = node->args()->list();
257 for ( QgsExpressionNode *argNode : argList )
258 {
259 if ( !argNode->isStatic( parent, context ) )
260 return false;
261 }
262 }
263
264 return true;
265}
266
267static QVariant fcnGenerateSeries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
268{
269 double start = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
270 double stop = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
271 double step = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
272
273 if ( step == 0.0 || ( step > 0.0 && start > stop ) || ( step < 0.0 && start < stop ) )
274 return QVariant();
275
276 QVariantList array;
277 int length = 1;
278
279 array << start;
280 double current = start + step;
281 while ( ( ( step > 0.0 && current <= stop ) || ( step < 0.0 && current >= stop ) ) && length <= 1000000 )
282 {
283 array << current;
284 current += step;
285 length++;
286 }
287
288 return array;
289}
290
291static QVariant fcnGetVariable( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
292{
293 if ( !context )
294 return QVariant();
295
296 const QString name = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
297
298 if ( name == QLatin1String( "feature" ) )
299 {
300 return context->hasFeature() ? QVariant::fromValue( context->feature() ) : QVariant();
301 }
302 else if ( name == QLatin1String( "id" ) )
303 {
304 return context->hasFeature() ? QVariant::fromValue( context->feature().id() ) : QVariant();
305 }
306 else if ( name == QLatin1String( "geometry" ) )
307 {
308 if ( !context->hasFeature() )
309 return QVariant();
310
311 const QgsFeature feature = context->feature();
312 return feature.hasGeometry() ? QVariant::fromValue( feature.geometry() ) : QVariant();
313 }
314 else
315 {
316 return context->variable( name );
317 }
318}
319
320static QVariant fcnEvalTemplate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
321{
322 QString templateString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
323 return QgsExpression::replaceExpressionText( templateString, context );
324}
325
326static QVariant fcnEval( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
327{
328 if ( !context )
329 return QVariant();
330
331 QString expString = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
332 QgsExpression expression( expString );
333 return expression.evaluate( context );
334}
335
336static QVariant fcnSqrt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
337{
338 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
339 return QVariant( std::sqrt( x ) );
340}
341
342static QVariant fcnAbs( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
343{
344 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
345 return QVariant( std::fabs( val ) );
346}
347
348static QVariant fcnRadians( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
349{
350 double deg = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
351 return ( deg * M_PI ) / 180;
352}
353static QVariant fcnDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
354{
355 double rad = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
356 return ( 180 * rad ) / M_PI;
357}
358static QVariant fcnSin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
359{
360 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
361 return QVariant( std::sin( x ) );
362}
363static QVariant fcnCos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
364{
365 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
366 return QVariant( std::cos( x ) );
367}
368static QVariant fcnTan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
369{
370 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
371 return QVariant( std::tan( x ) );
372}
373static QVariant fcnAsin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
374{
375 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
376 return QVariant( std::asin( x ) );
377}
378static QVariant fcnAcos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
379{
380 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
381 return QVariant( std::acos( x ) );
382}
383static QVariant fcnAtan( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
384{
385 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
386 return QVariant( std::atan( x ) );
387}
388static QVariant fcnAtan2( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
389{
390 double y = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
391 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
392 return QVariant( std::atan2( y, x ) );
393}
394static QVariant fcnExp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
395{
396 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
397 return QVariant( std::exp( x ) );
398}
399static QVariant fcnLn( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
400{
401 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
402 if ( x <= 0 )
403 return QVariant();
404 return QVariant( std::log( x ) );
405}
406static QVariant fcnLog10( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
407{
408 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
409 if ( x <= 0 )
410 return QVariant();
411 return QVariant( log10( x ) );
412}
413static QVariant fcnLog( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
414{
415 double b = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
416 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
417 if ( x <= 0 || b <= 0 )
418 return QVariant();
419 return QVariant( std::log( x ) / std::log( b ) );
420}
421static QVariant fcnRndF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
422{
423 double min = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
424 double max = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
425 if ( max < min )
426 return QVariant();
427
428 std::random_device rd;
429 std::mt19937_64 generator( rd() );
430
431 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
432 {
433 quint32 seed;
434 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
435 {
436 // if seed can be converted to int, we use as is
437 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
438 }
439 else
440 {
441 // if not, we hash string representation to int
442 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
443 std::hash<std::string> hasher;
444 seed = hasher( seedStr.toStdString() );
445 }
446 generator.seed( seed );
447 }
448
449 // Return a random double in the range [min, max] (inclusive)
450 double f = static_cast< double >( generator() ) / static_cast< double >( std::mt19937_64::max() );
451 return QVariant( min + f * ( max - min ) );
452}
453static QVariant fcnRnd( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
454{
455 qlonglong min = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
456 qlonglong max = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
457 if ( max < min )
458 return QVariant();
459
460 std::random_device rd;
461 std::mt19937_64 generator( rd() );
462
463 if ( !QgsExpressionUtils::isNull( values.at( 2 ) ) )
464 {
465 quint32 seed;
466 if ( QgsExpressionUtils::isIntSafe( values.at( 2 ) ) )
467 {
468 // if seed can be converted to int, we use as is
469 seed = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
470 }
471 else
472 {
473 // if not, we hash string representation to int
474 QString seedStr = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
475 std::hash<std::string> hasher;
476 seed = hasher( seedStr.toStdString() );
477 }
478 generator.seed( seed );
479 }
480
481 qint64 randomInteger = min + ( generator() % ( max - min + 1 ) );
482 if ( randomInteger > std::numeric_limits<int>::max() || randomInteger < -std::numeric_limits<int>::max() )
483 return QVariant( randomInteger );
484
485 // Prevent wrong conversion of QVariant. See #36412
486 return QVariant( int( randomInteger ) );
487}
488
489static QVariant fcnLinearScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
490{
491 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
492 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
493 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
494 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
495 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
496
497 if ( domainMin >= domainMax )
498 {
499 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
500 return QVariant();
501 }
502
503 // outside of domain?
504 if ( val >= domainMax )
505 {
506 return rangeMax;
507 }
508 else if ( val <= domainMin )
509 {
510 return rangeMin;
511 }
512
513 // calculate linear scale
514 double m = ( rangeMax - rangeMin ) / ( domainMax - domainMin );
515 double c = rangeMin - ( domainMin * m );
516
517 // Return linearly scaled value
518 return QVariant( m * val + c );
519}
520
521static QVariant fcnPolynomialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
522{
523 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
524 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
525 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
526 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
527 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
528 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
529
530 if ( domainMin >= domainMax )
531 {
532 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
533 return QVariant();
534 }
535 if ( exponent <= 0 )
536 {
537 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
538 return QVariant();
539 }
540
541 // outside of domain?
542 if ( val >= domainMax )
543 {
544 return rangeMax;
545 }
546 else if ( val <= domainMin )
547 {
548 return rangeMin;
549 }
550
551 // Return polynomially scaled value
552 return QVariant( ( ( rangeMax - rangeMin ) / std::pow( domainMax - domainMin, exponent ) ) * std::pow( val - domainMin, exponent ) + rangeMin );
553}
554
555static QVariant fcnExponentialScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
556{
557 double val = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
558 double domainMin = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
559 double domainMax = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
560 double rangeMin = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
561 double rangeMax = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
562 double exponent = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
563
564 if ( domainMin >= domainMax )
565 {
566 parent->setEvalErrorString( QObject::tr( "Domain max must be greater than domain min" ) );
567 return QVariant();
568 }
569 if ( exponent <= 0 )
570 {
571 parent->setEvalErrorString( QObject::tr( "Exponent must be greater than 0" ) );
572 return QVariant();
573 }
574
575 // outside of domain?
576 if ( val >= domainMax )
577 {
578 return rangeMax;
579 }
580 else if ( val <= domainMin )
581 {
582 return rangeMin;
583 }
584
585 // Return exponentially scaled value
586 double ratio = ( std::pow( exponent, val - domainMin ) - 1 ) / ( std::pow( exponent, domainMax - domainMin ) - 1 );
587 return QVariant( ( rangeMax - rangeMin ) * ratio + rangeMin );
588}
589
590static QVariant fcnMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
591{
592 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
593 double maxVal = std::numeric_limits<double>::quiet_NaN();
594 for ( const QVariant &val : values )
595 {
596 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
597 if ( std::isnan( maxVal ) )
598 {
599 maxVal = testVal;
600 }
601 else if ( !std::isnan( testVal ) )
602 {
603 maxVal = std::max( maxVal, testVal );
604 }
605 }
606
607 if ( !std::isnan( maxVal ) )
608 {
609 result = QVariant( maxVal );
610 }
611 return result;
612}
613
614static QVariant fcnMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
615{
616 QVariant result = QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
617 double minVal = std::numeric_limits<double>::quiet_NaN();
618 for ( const QVariant &val : values )
619 {
620 double testVal = QgsVariantUtils::isNull( val ) ? std::numeric_limits<double>::quiet_NaN() : QgsExpressionUtils::getDoubleValue( val, parent );
621 if ( std::isnan( minVal ) )
622 {
623 minVal = testVal;
624 }
625 else if ( !std::isnan( testVal ) )
626 {
627 minVal = std::min( minVal, testVal );
628 }
629 }
630
631 if ( !std::isnan( minVal ) )
632 {
633 result = QVariant( minVal );
634 }
635 return result;
636}
637
638static QVariant fcnAggregate( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
639{
640 //lazy eval, so we need to evaluate nodes now
641
642 //first node is layer id or name
643 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
645 QVariant value = node->eval( parent, context );
647
648 // TODO this expression function is NOT thread safe
650 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( value, context, parent );
652 if ( !vl )
653 {
654 parent->setEvalErrorString( QObject::tr( "Cannot find layer with name or ID '%1'" ).arg( value.toString() ) );
655 return QVariant();
656 }
657
658 // second node is aggregate type
659 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
661 value = node->eval( parent, context );
663 bool ok = false;
664 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
665 if ( !ok )
666 {
667 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
668 return QVariant();
669 }
670
671 // third node is subexpression (or field name)
672 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
674 QString subExpression = node->dump();
675
677 //optional forth node is filter
678 if ( values.count() > 3 )
679 {
680 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
682 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
683 if ( !nl || nl->value().isValid() )
684 parameters.filter = node->dump();
685 }
686
687 //optional fifth node is concatenator
688 if ( values.count() > 4 )
689 {
690 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
692 value = node->eval( parent, context );
694 parameters.delimiter = value.toString();
695 }
696
697 //optional sixth node is order by
698 QString orderBy;
699 if ( values.count() > 5 )
700 {
701 node = QgsExpressionUtils::getNode( values.at( 5 ), parent );
703 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
704 if ( !nl || nl->value().isValid() )
705 {
706 orderBy = node->dump();
707 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
708 }
709 }
710
711 QString aggregateError;
712 QVariant result;
713 if ( context )
714 {
715 QString cacheKey;
716 QgsExpression subExp( subExpression );
717 QgsExpression filterExp( parameters.filter );
718
719 const QSet< QString > filterVars = filterExp.referencedVariables();
720 const QSet< QString > subExpVars = subExp.referencedVariables();
721 QSet<QString> allVars = filterVars + subExpVars;
722
723 bool isStatic = true;
724 if ( filterVars.contains( QStringLiteral( "parent" ) )
725 || filterVars.contains( QString() )
726 || subExpVars.contains( QStringLiteral( "parent" ) )
727 || subExpVars.contains( QString() ) )
728 {
729 isStatic = false;
730 }
731 else
732 {
733 for ( const QString &varName : allVars )
734 {
735 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
736 if ( scope && !scope->isStatic( varName ) )
737 {
738 isStatic = false;
739 break;
740 }
741 }
742 }
743
744 if ( isStatic && ! parameters.orderBy.isEmpty() )
745 {
746 for ( const auto &orderByClause : std::as_const( parameters.orderBy ) )
747 {
748 const QgsExpression &orderByExpression { orderByClause.expression() };
749 if ( orderByExpression.referencedVariables().contains( QStringLiteral( "parent" ) ) || orderByExpression.referencedVariables().contains( QString() ) )
750 {
751 isStatic = false;
752 break;
753 }
754 }
755 }
756
757 if ( !isStatic )
758 {
759 bool ok = false;
760 const QString contextHash = context->uniqueHash( ok, allVars );
761 if ( ok )
762 {
763 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
764 orderBy, contextHash );
765 }
766 }
767 else
768 {
769 cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
770 }
771
772 if ( !cacheKey.isEmpty() && context->hasCachedValue( cacheKey ) )
773 {
774 return context->cachedValue( cacheKey );
775 }
776
777 QgsExpressionContext subContext( *context );
779 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
780 subContext.appendScope( subScope );
781 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &aggregateError );
782
783 if ( ok && !cacheKey.isEmpty() )
784 {
785 // important -- we should only store cached values when the expression is successfully calculated. Otherwise subsequent
786 // use of the expression context will happily grab the invalid QVariant cached value without realising that there was actually an error
787 // associated with it's calculation!
788 context->setCachedValue( cacheKey, result );
789 }
790 }
791 else
792 {
793 result = vl->aggregate( aggregate, subExpression, parameters, nullptr, &ok, nullptr, nullptr, &aggregateError );
794 }
795 if ( !ok )
796 {
797 if ( !aggregateError.isEmpty() )
798 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, aggregateError ) );
799 else
800 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
801 return QVariant();
802 }
803
804 return result;
805}
806
807static QVariant fcnAggregateRelation( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
808{
809 if ( !context )
810 {
811 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
812 return QVariant();
813 }
814
815 // first step - find current layer
816
817 // TODO this expression function is NOT thread safe
819 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
821 if ( !vl )
822 {
823 parent->setEvalErrorString( QObject::tr( "Cannot use relation aggregate function in this context" ) );
824 return QVariant();
825 }
826
827 //lazy eval, so we need to evaluate nodes now
828
829 //first node is relation name
830 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
832 QVariant value = node->eval( parent, context );
834 QString relationId = value.toString();
835 // check relation exists
836 QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationId ); // skip-keyword-check
837 if ( !relation.isValid() || relation.referencedLayer() != vl )
838 {
839 // check for relations by name
840 QList< QgsRelation > relations = QgsProject::instance()->relationManager()->relationsByName( relationId ); // skip-keyword-check
841 if ( relations.isEmpty() || relations.at( 0 ).referencedLayer() != vl )
842 {
843 parent->setEvalErrorString( QObject::tr( "Cannot find relation with id '%1'" ).arg( relationId ) );
844 return QVariant();
845 }
846 else
847 {
848 relation = relations.at( 0 );
849 }
850 }
851
852 QgsVectorLayer *childLayer = relation.referencingLayer();
853
854 // second node is aggregate type
855 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
857 value = node->eval( parent, context );
859 bool ok = false;
860 Qgis::Aggregate aggregate = QgsAggregateCalculator::stringToAggregate( QgsExpressionUtils::getStringValue( value, parent ), &ok );
861 if ( !ok )
862 {
863 parent->setEvalErrorString( QObject::tr( "No such aggregate '%1'" ).arg( value.toString() ) );
864 return QVariant();
865 }
866
867 //third node is subexpression (or field name)
868 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
870 QString subExpression = node->dump();
871
872 //optional fourth node is concatenator
874 if ( values.count() > 3 )
875 {
876 node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
878 value = node->eval( parent, context );
880 parameters.delimiter = value.toString();
881 }
882
883 //optional fifth node is order by
884 QString orderBy;
885 if ( values.count() > 4 )
886 {
887 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
889 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
890 if ( !nl || nl->value().isValid() )
891 {
892 orderBy = node->dump();
893 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
894 }
895 }
896
897 if ( !context->hasFeature() )
898 return QVariant();
899 QgsFeature f = context->feature();
900
901 parameters.filter = relation.getRelatedFeaturesFilter( f );
902
903 const QString cacheKey = QStringLiteral( "relagg:%1%:%2:%3:%4:%5:%6" ).arg( relationId, vl->id(),
904 QString::number( static_cast< int >( aggregate ) ),
905 subExpression,
906 parameters.filter,
907 orderBy );
908 if ( context->hasCachedValue( cacheKey ) )
909 return context->cachedValue( cacheKey );
910
911 QVariant result;
912 ok = false;
913
914
915 QgsExpressionContext subContext( *context );
916 QString error;
917 result = childLayer->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
918
919 if ( !ok )
920 {
921 if ( !error.isEmpty() )
922 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
923 else
924 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
925 return QVariant();
926 }
927
928 // cache value
929 context->setCachedValue( cacheKey, result );
930 return result;
931}
932
933
934static QVariant fcnAggregateGeneric( Qgis::Aggregate aggregate, const QVariantList &values, QgsAggregateCalculator::AggregateParameters parameters, const QgsExpressionContext *context, QgsExpression *parent, int orderByPos = -1 )
935{
936 if ( !context )
937 {
938 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
939 return QVariant();
940 }
941
942 // first step - find current layer
943
944 // TODO this expression function is NOT thread safe
946 QgsVectorLayer *vl = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
948 if ( !vl )
949 {
950 parent->setEvalErrorString( QObject::tr( "Cannot use aggregate function in this context" ) );
951 return QVariant();
952 }
953
954 //lazy eval, so we need to evaluate nodes now
955
956 //first node is subexpression (or field name)
957 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
959 QString subExpression = node->dump();
960
961 //optional second node is group by
962 QString groupBy;
963 if ( values.count() > 1 )
964 {
965 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
967 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
968 if ( !nl || nl->value().isValid() )
969 groupBy = node->dump();
970 }
971
972 //optional third node is filter
973 if ( values.count() > 2 )
974 {
975 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
977 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
978 if ( !nl || nl->value().isValid() )
979 parameters.filter = node->dump();
980 }
981
982 //optional order by node, if supported
983 QString orderBy;
984 if ( orderByPos >= 0 && values.count() > orderByPos )
985 {
986 node = QgsExpressionUtils::getNode( values.at( orderByPos ), parent );
988 QgsExpressionNodeLiteral *nl = dynamic_cast< QgsExpressionNodeLiteral * >( node );
989 if ( !nl || nl->value().isValid() )
990 {
991 orderBy = node->dump();
992 parameters.orderBy << QgsFeatureRequest::OrderByClause( orderBy );
993 }
994 }
995
996 // build up filter with group by
997
998 // find current group by value
999 if ( !groupBy.isEmpty() )
1000 {
1001 QgsExpression groupByExp( groupBy );
1002 QVariant groupByValue = groupByExp.evaluate( context );
1003 QString groupByClause = QStringLiteral( "%1 %2 %3" ).arg( groupBy,
1004 QgsVariantUtils::isNull( groupByValue ) ? QStringLiteral( "is" ) : QStringLiteral( "=" ),
1005 QgsExpression::quotedValue( groupByValue ) );
1006 if ( !parameters.filter.isEmpty() )
1007 parameters.filter = QStringLiteral( "(%1) AND (%2)" ).arg( parameters.filter, groupByClause );
1008 else
1009 parameters.filter = groupByClause;
1010 }
1011
1012 QgsExpression subExp( subExpression );
1013 QgsExpression filterExp( parameters.filter );
1014
1015 bool isStatic = true;
1016 const QSet<QString> refVars = filterExp.referencedVariables() + subExp.referencedVariables();
1017 for ( const QString &varName : refVars )
1018 {
1019 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
1020 if ( scope && !scope->isStatic( varName ) )
1021 {
1022 isStatic = false;
1023 break;
1024 }
1025 }
1026
1027 QString cacheKey;
1028 if ( !isStatic )
1029 {
1030 bool ok = false;
1031 const QString contextHash = context->uniqueHash( ok, refVars );
1032 if ( ok )
1033 {
1034 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5:%6" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter,
1035 orderBy, contextHash );
1036 }
1037 }
1038 else
1039 {
1040 cacheKey = QStringLiteral( "agg:%1:%2:%3:%4:%5" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter, orderBy );
1041 }
1042
1043 if ( context->hasCachedValue( cacheKey ) )
1044 return context->cachedValue( cacheKey );
1045
1046 QVariant result;
1047 bool ok = false;
1048
1049 QgsExpressionContext subContext( *context );
1051 subScope->setVariable( QStringLiteral( "parent" ), context->feature(), true );
1052 subContext.appendScope( subScope );
1053 QString error;
1054 result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok, nullptr, context->feedback(), &error );
1055
1056 if ( !ok )
1057 {
1058 if ( !error.isEmpty() )
1059 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1 (%2)" ).arg( subExpression, error ) );
1060 else
1061 parent->setEvalErrorString( QObject::tr( "Could not calculate aggregate for: %1" ).arg( subExpression ) );
1062 return QVariant();
1063 }
1064
1065 // cache value
1066 context->setCachedValue( cacheKey, result );
1067 return result;
1068}
1069
1070
1071static QVariant fcnAggregateCount( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1072{
1073 return fcnAggregateGeneric( Qgis::Aggregate::Count, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1074}
1075
1076static QVariant fcnAggregateCountDistinct( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1077{
1078 return fcnAggregateGeneric( Qgis::Aggregate::CountDistinct, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1079}
1080
1081static QVariant fcnAggregateCountMissing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1082{
1083 return fcnAggregateGeneric( Qgis::Aggregate::CountMissing, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1084}
1085
1086static QVariant fcnAggregateMin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1087{
1088 return fcnAggregateGeneric( Qgis::Aggregate::Min, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1089}
1090
1091static QVariant fcnAggregateMax( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1092{
1093 return fcnAggregateGeneric( Qgis::Aggregate::Max, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1094}
1095
1096static QVariant fcnAggregateSum( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1097{
1098 return fcnAggregateGeneric( Qgis::Aggregate::Sum, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1099}
1100
1101static QVariant fcnAggregateMean( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1102{
1103 return fcnAggregateGeneric( Qgis::Aggregate::Mean, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1104}
1105
1106static QVariant fcnAggregateMedian( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1107{
1108 return fcnAggregateGeneric( Qgis::Aggregate::Median, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1109}
1110
1111static QVariant fcnAggregateStdev( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1112{
1113 return fcnAggregateGeneric( Qgis::Aggregate::StDevSample, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1114}
1115
1116static QVariant fcnAggregateRange( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1117{
1118 return fcnAggregateGeneric( Qgis::Aggregate::Range, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1119}
1120
1121static QVariant fcnAggregateMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1122{
1123 return fcnAggregateGeneric( Qgis::Aggregate::Minority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1124}
1125
1126static QVariant fcnAggregateMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1127{
1128 return fcnAggregateGeneric( Qgis::Aggregate::Majority, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1129}
1130
1131static QVariant fcnAggregateQ1( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1132{
1133 return fcnAggregateGeneric( Qgis::Aggregate::FirstQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1134}
1135
1136static QVariant fcnAggregateQ3( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1137{
1138 return fcnAggregateGeneric( Qgis::Aggregate::ThirdQuartile, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1139}
1140
1141static QVariant fcnAggregateIQR( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1142{
1143 return fcnAggregateGeneric( Qgis::Aggregate::InterQuartileRange, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1144}
1145
1146static QVariant fcnAggregateMinLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1147{
1148 return fcnAggregateGeneric( Qgis::Aggregate::StringMinimumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1149}
1150
1151static QVariant fcnAggregateMaxLength( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1152{
1153 return fcnAggregateGeneric( Qgis::Aggregate::StringMaximumLength, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1154}
1155
1156static QVariant fcnAggregateCollectGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1157{
1158 return fcnAggregateGeneric( Qgis::Aggregate::GeometryCollect, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
1159}
1160
1161static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1162{
1164
1165 //fourth node is concatenator
1166 if ( values.count() > 3 )
1167 {
1168 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1170 QVariant value = node->eval( parent, context );
1172 parameters.delimiter = value.toString();
1173 }
1174
1175 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenate, values, parameters, context, parent, 4 );
1176}
1177
1178static QVariant fcnAggregateStringConcatUnique( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1179{
1181
1182 //fourth node is concatenator
1183 if ( values.count() > 3 )
1184 {
1185 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 3 ), parent );
1187 QVariant value = node->eval( parent, context );
1189 parameters.delimiter = value.toString();
1190 }
1191
1192 return fcnAggregateGeneric( Qgis::Aggregate::StringConcatenateUnique, values, parameters, context, parent, 4 );
1193}
1194
1195static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1196{
1197 return fcnAggregateGeneric( Qgis::Aggregate::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent, 3 );
1198}
1199
1200static QVariant fcnMapScale( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1201{
1202 if ( !context )
1203 return QVariant();
1204
1205 QVariant scale = context->variable( QStringLiteral( "map_scale" ) );
1206 bool ok = false;
1207 if ( QgsVariantUtils::isNull( scale ) )
1208 return QVariant();
1209
1210 const double v = scale.toDouble( &ok );
1211 if ( ok )
1212 return v;
1213 return QVariant();
1214}
1215
1216static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1217{
1218 double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1219 double testValue = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1220 double maxValue = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1221
1222 // force testValue to sit inside the range specified by the min and max value
1223 if ( testValue <= minValue )
1224 {
1225 return QVariant( minValue );
1226 }
1227 else if ( testValue >= maxValue )
1228 {
1229 return QVariant( maxValue );
1230 }
1231 else
1232 {
1233 return QVariant( testValue );
1234 }
1235}
1236
1237static QVariant fcnFloor( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1238{
1239 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1240 return QVariant( std::floor( x ) );
1241}
1242
1243static QVariant fcnCeil( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1244{
1245 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1246 return QVariant( std::ceil( x ) );
1247}
1248
1249static QVariant fcnToBool( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1250{
1251 const QVariant value = values.at( 0 );
1252 if ( QgsExpressionUtils::isNull( value.isValid() ) )
1253 {
1254 return QVariant( false );
1255 }
1256 else if ( value.userType() == QMetaType::QString )
1257 {
1258 // Capture strings to avoid a '0' string value casted to 0 and wrongly returning false
1259 return QVariant( !value.toString().isEmpty() );
1260 }
1261 else if ( QgsExpressionUtils::isList( value ) )
1262 {
1263 return !value.toList().isEmpty();
1264 }
1265 return QVariant( value.toBool() );
1266}
1267static QVariant fcnToInt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1268{
1269 return QVariant( QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) );
1270}
1271static QVariant fcnToReal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1272{
1273 return QVariant( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
1274}
1275static QVariant fcnToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1276{
1277 return QVariant( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ) );
1278}
1279
1280static QVariant fcnToDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1281{
1282 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1283 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1284 if ( format.isEmpty() && !language.isEmpty() )
1285 {
1286 parent->setEvalErrorString( QObject::tr( "A format is required to convert to DateTime when the language is specified" ) );
1287 return QVariant( QDateTime() );
1288 }
1289
1290 if ( format.isEmpty() && language.isEmpty() )
1291 return QVariant( QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent ) );
1292
1293 QString datetimestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1294 QLocale locale = QLocale();
1295 if ( !language.isEmpty() )
1296 {
1297 locale = QLocale( language );
1298 }
1299
1300 QDateTime datetime = locale.toDateTime( datetimestring, format );
1301 if ( !datetime.isValid() )
1302 {
1303 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to DateTime" ).arg( datetimestring ) );
1304 datetime = QDateTime();
1305 }
1306 return QVariant( datetime );
1307}
1308
1309static QVariant fcnMakeDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1310{
1311 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1312 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1313 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1314
1315 const QDate date( year, month, day );
1316 if ( !date.isValid() )
1317 {
1318 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1319 return QVariant();
1320 }
1321 return QVariant( date );
1322}
1323
1324static QVariant fcnMakeTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1325{
1326 const int hours = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1327 const int minutes = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1328 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1329
1330 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1331 if ( !time.isValid() )
1332 {
1333 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1334 return QVariant();
1335 }
1336 return QVariant( time );
1337}
1338
1339static QVariant fcnMakeDateTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1340{
1341 const int year = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
1342 const int month = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1343 const int day = QgsExpressionUtils::getIntValue( values.at( 2 ), parent );
1344 const int hours = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
1345 const int minutes = QgsExpressionUtils::getIntValue( values.at( 4 ), parent );
1346 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1347
1348 const QDate date( year, month, day );
1349 if ( !date.isValid() )
1350 {
1351 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid date" ).arg( year ).arg( month ).arg( day ) );
1352 return QVariant();
1353 }
1354 const QTime time( hours, minutes, std::floor( seconds ), ( seconds - std::floor( seconds ) ) * 1000 );
1355 if ( !time.isValid() )
1356 {
1357 parent->setEvalErrorString( QObject::tr( "'%1-%2-%3' is not a valid time" ).arg( hours ).arg( minutes ).arg( seconds ) );
1358 return QVariant();
1359 }
1360 return QVariant( QDateTime( date, time ) );
1361}
1362
1363static QVariant fcnTimeZoneFromId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1364{
1365 const QString timeZoneId = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1366
1367 QTimeZone tz;
1368
1369#if QT_FEATURE_timezone > 0
1370 if ( !timeZoneId.isEmpty() )
1371 {
1372 tz = QTimeZone( timeZoneId.toUtf8() );
1373 }
1374
1375 if ( !tz.isValid() )
1376 {
1377 parent->setEvalErrorString( QObject::tr( "'%1' is not a valid time zone ID" ).arg( timeZoneId ) );
1378 return QVariant();
1379 }
1380
1381#else
1382 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneFromId" ) );
1383#endif
1384 return QVariant::fromValue( tz );
1385}
1386
1387static QVariant fcnGetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1388{
1389#if QT_FEATURE_timezone > 0
1390 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1391 if ( datetime.isValid() )
1392 {
1393 return QVariant::fromValue( datetime.timeZone() );
1394 }
1395 return QVariant();
1396#else
1397 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnGetTimeZone" ) );
1398 return QVariant();
1399#endif
1400}
1401
1402static QVariant fcnSetTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1403{
1404#if QT_FEATURE_timezone > 0
1405 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1406 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1407 if ( datetime.isValid() && tz.isValid() )
1408 {
1409 datetime.setTimeZone( tz );
1410 return QVariant::fromValue( datetime );
1411 }
1412 return QVariant();
1413#else
1414 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnSetTimeZone" ) );
1415 return QVariant();
1416#endif
1417}
1418
1419static QVariant fcnConvertTimeZone( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1420{
1421#if QT_FEATURE_timezone > 0
1422 const QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
1423 const QTimeZone tz = QgsExpressionUtils::getTimeZoneValue( values.at( 1 ), parent );
1424 if ( datetime.isValid() && tz.isValid() )
1425 {
1426 return QVariant::fromValue( datetime.toTimeZone( tz ) );
1427 }
1428 return QVariant();
1429#else
1430 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnConvertTimeZone" ) );
1431 return QVariant();
1432#endif
1433}
1434
1435static QVariant fcnTimeZoneToId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1436{
1437#if QT_FEATURE_timezone > 0
1438 const QTimeZone timeZone = QgsExpressionUtils::getTimeZoneValue( values.at( 0 ), parent );
1439 if ( timeZone.isValid() )
1440 {
1441 return QString( timeZone.id() );
1442 }
1443 return QVariant();
1444#else
1445 parent->setEvalErrorString( QObject::tr( "Qt is built without Qt timezone support, cannot use fcnTimeZoneToId" ) );
1446 return QVariant();
1447#endif
1448}
1449
1450static QVariant fcnMakeInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1451{
1452 const double years = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
1453 const double months = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
1454 const double weeks = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1455 const double days = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
1456 const double hours = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
1457 const double minutes = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
1458 const double seconds = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
1459
1460 return QVariant::fromValue( QgsInterval( years, months, weeks, days, hours, minutes, seconds ) );
1461}
1462
1463static QVariant fcnCoalesce( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1464{
1465 for ( const QVariant &value : values )
1466 {
1467 if ( QgsVariantUtils::isNull( value ) )
1468 continue;
1469 return value;
1470 }
1471 return QVariant();
1472}
1473
1474static QVariant fcnNullIf( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1475{
1476 const QVariant val1 = values.at( 0 );
1477 const QVariant val2 = values.at( 1 );
1478
1479 if ( val1 == val2 )
1480 return QVariant();
1481 else
1482 return val1;
1483}
1484
1485static QVariant fcnLower( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1486{
1487 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1488 return QVariant( str.toLower() );
1489}
1490static QVariant fcnUpper( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1491{
1492 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1493 return QVariant( str.toUpper() );
1494}
1495static QVariant fcnTitle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1496{
1497 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1498 QStringList elems = str.split( ' ' );
1499 for ( int i = 0; i < elems.size(); i++ )
1500 {
1501 if ( elems[i].size() > 1 )
1502 elems[i] = elems[i].at( 0 ).toUpper() + elems[i].mid( 1 ).toLower();
1503 }
1504 return QVariant( elems.join( QLatin1Char( ' ' ) ) );
1505}
1506
1507static QVariant fcnTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1508{
1509 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1510 return QVariant( str.trimmed() );
1511}
1512
1513static QVariant fcnLTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1514{
1515 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1516
1517 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1518
1519 const QRegularExpression re( QStringLiteral( "^([%1]*)" ).arg( QRegularExpression::escape( characters ) ) );
1520 str.replace( re, QString() );
1521 return QVariant( str );
1522}
1523
1524static QVariant fcnRTrim( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1525{
1526 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1527
1528 const QString characters = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1529
1530 const QRegularExpression re( QStringLiteral( "([%1]*)$" ).arg( QRegularExpression::escape( characters ) ) );
1531 str.replace( re, QString() );
1532 return QVariant( str );
1533}
1534
1535static QVariant fcnLevenshtein( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1536{
1537 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1538 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1539 return QVariant( QgsStringUtils::levenshteinDistance( string1, string2, true ) );
1540}
1541
1542static QVariant fcnLCS( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1543{
1544 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1545 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1546 return QVariant( QgsStringUtils::longestCommonSubstring( string1, string2, true ) );
1547}
1548
1549static QVariant fcnHamming( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1550{
1551 QString string1 = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1552 QString string2 = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1553 int dist = QgsStringUtils::hammingDistance( string1, string2 );
1554 return ( dist < 0 ? QVariant() : QVariant( QgsStringUtils::hammingDistance( string1, string2, true ) ) );
1555}
1556
1557static QVariant fcnSoundex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1558{
1559 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1560 return QVariant( QgsStringUtils::soundex( string ) );
1561}
1562
1563static QVariant fcnChar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1564{
1565 QChar character = QChar( QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent ) );
1566 return QVariant( QString( character ) );
1567}
1568
1569static QVariant fcnAscii( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1570{
1571 QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1572
1573 if ( value.isEmpty() )
1574 {
1575 return QVariant();
1576 }
1577
1578 int res = value.at( 0 ).unicode();
1579 return QVariant( res );
1580}
1581
1582static QVariant fcnWordwrap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1583{
1584 if ( values.length() == 2 || values.length() == 3 )
1585 {
1586 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1587 qlonglong wrap = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1588
1589 QString customdelimiter = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1590
1591 return QgsStringUtils::wordWrap( str, static_cast< int >( wrap ), wrap > 0, customdelimiter );
1592 }
1593
1594 return QVariant();
1595}
1596
1597static QVariant fcnLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1598{
1599 // two variants, one for geometry, one for string
1600
1601 //geometry variant
1602 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
1603 if ( !geom.isNull() )
1604 {
1605 if ( geom.type() == Qgis::GeometryType::Line )
1606 return QVariant( geom.length() );
1607 else
1608 return QVariant();
1609 }
1610
1611 //otherwise fall back to string variant
1612 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1613 return QVariant( str.length() );
1614}
1615
1616static QVariant fcnLength3D( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1617{
1618 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
1619
1620 if ( geom.type() != Qgis::GeometryType::Line )
1621 return QVariant();
1622
1623 double totalLength = 0;
1624 for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
1625 {
1627 {
1628 totalLength += line->length3D();
1629 }
1630 else
1631 {
1632 std::unique_ptr< QgsLineString > segmentized( qgsgeometry_cast< const QgsCurve * >( *it )->curveToLine() );
1633 totalLength += segmentized->length3D();
1634 }
1635 }
1636
1637 return totalLength;
1638}
1639
1640
1641static QVariant fcnRepeat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1642{
1643 const QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1644 const qlonglong number = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
1645 return string.repeated( std::max( static_cast< int >( number ), 0 ) );
1646}
1647
1648static QVariant fcnReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1649{
1650 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
1651 {
1652 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1653 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
1654 QVector< QPair< QString, QString > > mapItems;
1655
1656 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
1657 {
1658 mapItems.append( qMakePair( it.key(), it.value().toString() ) );
1659 }
1660
1661 // larger keys should be replaced first since they may contain whole smaller keys
1662 std::sort( mapItems.begin(),
1663 mapItems.end(),
1664 []( const QPair< QString, QString > &pair1,
1665 const QPair< QString, QString > &pair2 )
1666 {
1667 return ( pair1.first.length() > pair2.first.length() );
1668 } );
1669
1670 for ( auto it = mapItems.constBegin(); it != mapItems.constEnd(); ++it )
1671 {
1672 str = str.replace( it->first, it->second );
1673 }
1674
1675 return QVariant( str );
1676 }
1677 else if ( values.count() == 3 )
1678 {
1679 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1680 QVariantList before;
1681 QVariantList after;
1682 bool isSingleReplacement = false;
1683
1684 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
1685 {
1686 before = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1687 }
1688 else
1689 {
1690 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
1691 }
1692
1693 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
1694 {
1695 after = QVariantList() << QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1696 isSingleReplacement = true;
1697 }
1698 else
1699 {
1700 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
1701 }
1702
1703 if ( !isSingleReplacement && before.length() != after.length() )
1704 {
1705 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
1706 return QVariant();
1707 }
1708
1709 for ( int i = 0; i < before.length(); i++ )
1710 {
1711 str = str.replace( before.at( i ).toString(), after.at( isSingleReplacement ? 0 : i ).toString() );
1712 }
1713
1714 return QVariant( str );
1715 }
1716 else
1717 {
1718 parent->setEvalErrorString( QObject::tr( "Function replace requires 2 or 3 arguments" ) );
1719 return QVariant();
1720 }
1721}
1722
1723static QVariant fcnRegexpReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1724{
1725 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1726 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1727 QString after = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1728
1729 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1730 if ( !re.isValid() )
1731 {
1732 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1733 return QVariant();
1734 }
1735 return QVariant( str.replace( re, after ) );
1736}
1737
1738static QVariant fcnRegexpMatch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1739{
1740 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1741 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1742
1743 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1744 if ( !re.isValid() )
1745 {
1746 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1747 return QVariant();
1748 }
1749 return QVariant( ( str.indexOf( re ) + 1 ) );
1750}
1751
1752static QVariant fcnRegexpMatches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1753{
1754 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1755 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1756 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
1757
1758 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1759 if ( !re.isValid() )
1760 {
1761 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1762 return QVariant();
1763 }
1764
1765 QRegularExpressionMatch matches = re.match( str );
1766 if ( matches.hasMatch() )
1767 {
1768 QVariantList array;
1769 QStringList list = matches.capturedTexts();
1770
1771 // Skip the first string to only return captured groups
1772 for ( QStringList::const_iterator it = ++list.constBegin(); it != list.constEnd(); ++it )
1773 {
1774 array += ( !( *it ).isEmpty() ) ? *it : empty;
1775 }
1776
1777 return QVariant( array );
1778 }
1779 else
1780 {
1781 return QVariant();
1782 }
1783}
1784
1785static QVariant fcnRegexpSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1786{
1787 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1788 QString regexp = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
1789
1790 QRegularExpression re( regexp, QRegularExpression::UseUnicodePropertiesOption );
1791 if ( !re.isValid() )
1792 {
1793 parent->setEvalErrorString( QObject::tr( "Invalid regular expression '%1': %2" ).arg( regexp, re.errorString() ) );
1794 return QVariant();
1795 }
1796
1797 // extract substring
1798 QRegularExpressionMatch match = re.match( str );
1799 if ( match.hasMatch() )
1800 {
1801 // return first capture
1802 if ( match.lastCapturedIndex() > 0 )
1803 {
1804 // a capture group was present, so use that
1805 return QVariant( match.captured( 1 ) );
1806 }
1807 else
1808 {
1809 // no capture group, so using all match
1810 return QVariant( match.captured( 0 ) );
1811 }
1812 }
1813 else
1814 {
1815 return QVariant( "" );
1816 }
1817}
1818
1819static QVariant fcnUuid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
1820{
1821 QString uuid = QUuid::createUuid().toString();
1822 if ( values.at( 0 ).toString().compare( QStringLiteral( "WithoutBraces" ), Qt::CaseInsensitive ) == 0 )
1823 uuid = QUuid::createUuid().toString( QUuid::StringFormat::WithoutBraces );
1824 else if ( values.at( 0 ).toString().compare( QStringLiteral( "Id128" ), Qt::CaseInsensitive ) == 0 )
1825 uuid = QUuid::createUuid().toString( QUuid::StringFormat::Id128 );
1826 return uuid;
1827}
1828
1829static QVariant fcnSubstr( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
1830{
1831 if ( !values.at( 0 ).isValid() || !values.at( 1 ).isValid() )
1832 return QVariant();
1833
1834 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
1835 int from = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1836
1837 int len = 0;
1838 if ( values.at( 2 ).isValid() )
1839 len = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
1840 else
1841 len = str.size();
1842
1843 if ( from < 0 )
1844 {
1845 from = str.size() + from;
1846 if ( from < 0 )
1847 {
1848 from = 0;
1849 }
1850 }
1851 else if ( from > 0 )
1852 {
1853 //account for the fact that substr() starts at 1
1854 from -= 1;
1855 }
1856
1857 if ( len < 0 )
1858 {
1859 len = str.size() + len - from;
1860 if ( len < 0 )
1861 {
1862 len = 0;
1863 }
1864 }
1865
1866 return QVariant( str.mid( from, len ) );
1867}
1868static QVariant fcnFeatureId( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1869{
1870 FEAT_FROM_CONTEXT( context, f )
1871 return QVariant( f.id() );
1872}
1873
1874static QVariant fcnRasterValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1875{
1876 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1877 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
1878 bool foundLayer = false;
1879 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, geom]( QgsMapLayer * mapLayer )
1880 {
1881 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( mapLayer );
1882 if ( !layer || !layer->dataProvider() )
1883 {
1884 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1885 return QVariant();
1886 }
1887
1888 if ( bandNb < 1 || bandNb > layer->bandCount() )
1889 {
1890 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster band number." ) );
1891 return QVariant();
1892 }
1893
1894 if ( geom.isNull() || geom.type() != Qgis::GeometryType::Point )
1895 {
1896 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid point geometry." ) );
1897 return QVariant();
1898 }
1899
1900 QgsPointXY point = geom.asPoint();
1901 if ( geom.isMultipart() )
1902 {
1903 QgsMultiPointXY multiPoint = geom.asMultiPoint();
1904 if ( multiPoint.count() == 1 )
1905 {
1906 point = multiPoint[0];
1907 }
1908 else
1909 {
1910 // if the geometry contains more than one part, return an undefined value
1911 return QVariant();
1912 }
1913 }
1914
1915 double value = layer->dataProvider()->sample( point, bandNb );
1916 return std::isnan( value ) ? QVariant() : value;
1917 },
1918 foundLayer );
1919
1920 if ( !foundLayer )
1921 {
1922 parent->setEvalErrorString( QObject::tr( "Function `raster_value` requires a valid raster layer." ) );
1923 return QVariant();
1924 }
1925 else
1926 {
1927 return res;
1928 }
1929}
1930
1931static QVariant fcnRasterAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1932{
1933 const int bandNb = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
1934 const double value = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
1935
1936 bool foundLayer = false;
1937 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, bandNb, value]( QgsMapLayer * mapLayer )-> QVariant
1938 {
1939 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer *>( mapLayer );
1940 if ( !layer || !layer->dataProvider() )
1941 {
1942 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1943 return QVariant();
1944 }
1945
1946 if ( bandNb < 1 || bandNb > layer->bandCount() )
1947 {
1948 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster band number." ) );
1949 return QVariant();
1950 }
1951
1952 if ( std::isnan( value ) )
1953 {
1954 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster value." ) );
1955 return QVariant();
1956 }
1957
1958 if ( ! layer->dataProvider()->attributeTable( bandNb ) )
1959 {
1960 return QVariant();
1961 }
1962
1963 const QVariantList data = layer->dataProvider()->attributeTable( bandNb )->row( value );
1964 if ( data.isEmpty() )
1965 {
1966 return QVariant();
1967 }
1968
1969 QVariantMap result;
1970 const QList<QgsRasterAttributeTable::Field> fields { layer->dataProvider()->attributeTable( bandNb )->fields() };
1971 for ( int idx = 0; idx < static_cast<int>( fields.count( ) ) && idx < static_cast<int>( data.count() ); ++idx )
1972 {
1973 const QgsRasterAttributeTable::Field field { fields.at( idx ) };
1974 if ( field.isColor() || field.isRamp() )
1975 {
1976 continue;
1977 }
1978 result.insert( fields.at( idx ).name, data.at( idx ) );
1979 }
1980
1981 return result;
1982 }, foundLayer );
1983
1984 if ( !foundLayer )
1985 {
1986 parent->setEvalErrorString( QObject::tr( "Function `raster_attributes` requires a valid raster layer." ) );
1987 return QVariant();
1988 }
1989 else
1990 {
1991 return res;
1992 }
1993}
1994
1995static QVariant fcnFeature( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
1996{
1997 if ( !context )
1998 return QVariant();
1999
2000 return context->feature();
2001}
2002
2003static QVariant fcnAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2004{
2005 QgsFeature feature;
2006 QString attr;
2007 if ( values.size() == 1 )
2008 {
2009 attr = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2010 feature = context->feature();
2011 }
2012 else if ( values.size() == 2 )
2013 {
2014 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2015 attr = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2016 }
2017 else
2018 {
2019 parent->setEvalErrorString( QObject::tr( "Function `attribute` requires one or two parameters. %n given.", nullptr, values.length() ) );
2020 return QVariant();
2021 }
2022
2023 return feature.attribute( attr );
2024}
2025
2026static QVariant fcnMapToHtmlTable( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2027{
2028 QString table { R"html(
2029 <table>
2030 <thead>
2031 <tr><th>%1</th></tr>
2032 </thead>
2033 <tbody>
2034 <tr><td>%2</td></tr>
2035 </tbody>
2036 </table>)html" };
2037 QVariantMap dict;
2038 if ( values.size() == 1 )
2039 {
2040 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2041 }
2042 else
2043 {
2044 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_table` requires one parameter. %n given.", nullptr, values.length() ) );
2045 return QVariant();
2046 }
2047
2048 if ( dict.isEmpty() )
2049 {
2050 return QVariant();
2051 }
2052
2053 QStringList headers;
2054 QStringList cells;
2055
2056 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2057 {
2058 headers.push_back( it.key().toHtmlEscaped() );
2059 cells.push_back( it.value().toString( ).toHtmlEscaped() );
2060 }
2061
2062 return table.arg( headers.join( QLatin1String( "</th><th>" ) ), cells.join( QLatin1String( "</td><td>" ) ) );
2063}
2064
2065static QVariant fcnMapToHtmlDefinitionList( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2066{
2067 QString table { R"html(
2068 <dl>
2069 %1
2070 </dl>)html" };
2071 QVariantMap dict;
2072 if ( values.size() == 1 )
2073 {
2074 dict = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
2075 }
2076 else
2077 {
2078 parent->setEvalErrorString( QObject::tr( "Function `map_to_html_dl` requires one parameter. %n given.", nullptr, values.length() ) );
2079 return QVariant();
2080 }
2081
2082 if ( dict.isEmpty() )
2083 {
2084 return QVariant();
2085 }
2086
2087 QString rows;
2088
2089 for ( auto it = dict.cbegin(); it != dict.cend(); ++it )
2090 {
2091 rows.append( QStringLiteral( "<dt>%1</dt><dd>%2</dd>" ).arg( it.key().toHtmlEscaped(), it.value().toString().toHtmlEscaped() ) );
2092 }
2093
2094 return table.arg( rows );
2095}
2096
2097static QVariant fcnValidateFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2098{
2099 QVariant layer;
2100 if ( values.size() < 1 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2101 {
2102 layer = context->variable( QStringLiteral( "layer" ) );
2103 }
2104 else
2105 {
2106 //first node is layer id or name
2107 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
2109 layer = node->eval( parent, context );
2111 }
2112
2113 QgsFeature feature;
2114 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2115 {
2116 feature = context->feature();
2117 }
2118 else
2119 {
2120 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2121 }
2122
2124 const QString strength = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).toLower();
2125 if ( strength == QLatin1String( "hard" ) )
2126 {
2128 }
2129 else if ( strength == QLatin1String( "soft" ) )
2130 {
2132 }
2133
2134 bool foundLayer = false;
2135 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2136 {
2137 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2138 if ( !layer )
2139 {
2140 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2141 return QVariant();
2142 }
2143
2144 const QgsFields fields = layer->fields();
2145 bool valid = true;
2146 for ( int i = 0; i < fields.size(); i++ )
2147 {
2148 QStringList errors;
2149 valid = QgsVectorLayerUtils::validateAttribute( layer, feature, i, errors, constraintStrength );
2150 if ( !valid )
2151 {
2152 break;
2153 }
2154 }
2155
2156 return valid;
2157 }, foundLayer );
2158
2159 if ( !foundLayer )
2160 {
2161 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2162 return QVariant();
2163 }
2164
2165 return res;
2166}
2167
2168static QVariant fcnValidateAttribute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2169{
2170 QVariant layer;
2171 if ( values.size() < 2 || QgsVariantUtils::isNull( values.at( 1 ) ) )
2172 {
2173 layer = context->variable( QStringLiteral( "layer" ) );
2174 }
2175 else
2176 {
2177 //first node is layer id or name
2178 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
2180 layer = node->eval( parent, context );
2182 }
2183
2184 QgsFeature feature;
2185 if ( values.size() < 3 || QgsVariantUtils::isNull( values.at( 2 ) ) )
2186 {
2187 feature = context->feature();
2188 }
2189 else
2190 {
2191 feature = QgsExpressionUtils::getFeature( values.at( 2 ), parent );
2192 }
2193
2195 const QString strength = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).toLower();
2196 if ( strength == QLatin1String( "hard" ) )
2197 {
2199 }
2200 else if ( strength == QLatin1String( "soft" ) )
2201 {
2203 }
2204
2205 const QString attributeName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2206
2207 bool foundLayer = false;
2208 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [parent, feature, attributeName, constraintStrength]( QgsMapLayer * mapLayer ) -> QVariant
2209 {
2210 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2211 if ( !layer )
2212 {
2213 return QVariant();
2214 }
2215
2216 const int fieldIndex = layer->fields().indexFromName( attributeName );
2217 if ( fieldIndex == -1 )
2218 {
2219 parent->setEvalErrorString( QObject::tr( "The attribute name did not match any field for the given feature" ) );
2220 return QVariant();
2221 }
2222
2223 QStringList errors;
2224 bool valid = QgsVectorLayerUtils::validateAttribute( layer, feature, fieldIndex, errors, constraintStrength );
2225 return valid;
2226 }, foundLayer );
2227
2228 if ( !foundLayer )
2229 {
2230 parent->setEvalErrorString( QObject::tr( "No layer provided to conduct constraints checks" ) );
2231 return QVariant();
2232 }
2233
2234 return res;
2235}
2236
2237static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2238{
2239 QgsFeature feature;
2240 if ( values.size() == 0 || QgsVariantUtils::isNull( values.at( 0 ) ) )
2241 {
2242 feature = context->feature();
2243 }
2244 else
2245 {
2246 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2247 }
2248
2249 const QgsFields fields = feature.fields();
2250 QVariantMap result;
2251 for ( int i = 0; i < fields.count(); ++i )
2252 {
2253 result.insert( fields.at( i ).name(), feature.attribute( i ) );
2254 }
2255 return result;
2256}
2257
2258static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2259{
2260 QgsVectorLayer *layer = nullptr;
2261 QgsFeature feature;
2262
2263 // TODO this expression function is NOT thread safe
2265 if ( values.isEmpty() )
2266 {
2267 feature = context->feature();
2268 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2269 }
2270 else if ( values.size() == 1 )
2271 {
2272 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2273 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2274 }
2275 else if ( values.size() == 2 )
2276 {
2277 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2278 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2279 }
2280 else
2281 {
2282 parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2283 return QVariant();
2284 }
2286
2287 if ( !layer )
2288 {
2289 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
2290 return QVariant();
2291 }
2292
2293 if ( !feature.isValid() )
2294 {
2295 parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
2296 return QVariant();
2297 }
2298
2299 const QgsFields fields = feature.fields();
2300 QVariantMap result;
2301 for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
2302 {
2303 const QString fieldName { fields.at( fieldIndex ).name() };
2304 const QVariant attributeVal = feature.attribute( fieldIndex );
2305 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
2306 if ( context && context->hasCachedValue( cacheValueKey ) )
2307 {
2308 result.insert( fieldName, context->cachedValue( cacheValueKey ) );
2309 }
2310 else
2311 {
2312 const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
2314 QVariant cache;
2315 if ( context )
2316 {
2317 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
2318
2319 if ( !context->hasCachedValue( cacheKey ) )
2320 {
2321 cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
2322 context->setCachedValue( cacheKey, cache );
2323 }
2324 else
2325 {
2326 cache = context->cachedValue( cacheKey );
2327 }
2328 }
2329 QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
2330
2331 result.insert( fields.at( fieldIndex ).name(), value );
2332
2333 if ( context )
2334 {
2335 context->setCachedValue( cacheValueKey, value );
2336 }
2337
2338 }
2339 }
2340 return result;
2341}
2342
2343static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
2344{
2345 QgsVectorLayer *layer = nullptr;
2346 QgsFeature feature;
2347 bool evaluate = true;
2348
2349 // TODO this expression function is NOT thread safe
2351 if ( values.isEmpty() )
2352 {
2353 feature = context->feature();
2354 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2355 }
2356 else if ( values.size() == 1 )
2357 {
2358 layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
2359 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2360 }
2361 else if ( values.size() == 2 )
2362 {
2363 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2364 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2365 }
2366 else if ( values.size() == 3 )
2367 {
2368 layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), context, parent );
2369 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2370 evaluate = values.value( 2 ).toBool();
2371 }
2372 else
2373 {
2374 if ( isMaptip )
2375 {
2376 parent->setEvalErrorString( QObject::tr( "Function `maptip` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2377 }
2378 else
2379 {
2380 parent->setEvalErrorString( QObject::tr( "Function `display` requires no more than three parameters. %n given.", nullptr, values.length() ) );
2381 }
2382 return QVariant();
2383 }
2384
2385 if ( !layer )
2386 {
2387 parent->setEvalErrorString( QObject::tr( "The layer is not valid." ) );
2388 return QVariant( );
2389 }
2391
2392 if ( !feature.isValid() )
2393 {
2394 parent->setEvalErrorString( QObject::tr( "The feature is not valid." ) );
2395 return QVariant( );
2396 }
2397
2398 if ( ! evaluate )
2399 {
2400 if ( isMaptip )
2401 {
2402 return layer->mapTipTemplate();
2403 }
2404 else
2405 {
2406 return layer->displayExpression();
2407 }
2408 }
2409
2410 QgsExpressionContext subContext( *context );
2411 subContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
2412 subContext.setFeature( feature );
2413
2414 if ( isMaptip )
2415 {
2416 return QgsExpression::replaceExpressionText( layer->mapTipTemplate(), &subContext );
2417 }
2418 else
2419 {
2420 QgsExpression exp( layer->displayExpression() );
2421 exp.prepare( &subContext );
2422 return exp.evaluate( &subContext ).toString();
2423 }
2424}
2425
2426static QVariant fcnFeatureDisplayExpression( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2427{
2428 return fcnCoreFeatureMaptipDisplay( values, context, parent, false );
2429}
2430
2431static QVariant fcnFeatureMaptip( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2432{
2433 return fcnCoreFeatureMaptipDisplay( values, context, parent, true );
2434}
2435
2436static QVariant fcnIsSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2437{
2438 QgsFeature feature;
2439 QVariant layer;
2440 if ( values.isEmpty() )
2441 {
2442 feature = context->feature();
2443 layer = context->variable( QStringLiteral( "layer" ) );
2444 }
2445 else if ( values.size() == 1 )
2446 {
2447 feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
2448 layer = context->variable( QStringLiteral( "layer" ) );
2449 }
2450 else if ( values.size() == 2 )
2451 {
2452 feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
2453 layer = values.at( 0 );
2454 }
2455 else
2456 {
2457 parent->setEvalErrorString( QObject::tr( "Function `is_selected` requires no more than two parameters. %n given.", nullptr, values.length() ) );
2458 return QVariant();
2459 }
2460
2461 bool foundLayer = false;
2462 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, [feature]( QgsMapLayer * mapLayer ) -> QVariant
2463 {
2464 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2465 if ( !layer || !feature.isValid() )
2466 {
2467 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2468 }
2469
2470 return layer->selectedFeatureIds().contains( feature.id() );
2471 }, foundLayer );
2472 if ( !foundLayer )
2473 return QgsVariantUtils::createNullVariant( QMetaType::Type::Bool );
2474 else
2475 return res;
2476}
2477
2478static QVariant fcnNumSelected( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2479{
2480 QVariant layer;
2481
2482 if ( values.isEmpty() )
2483 layer = context->variable( QStringLiteral( "layer" ) );
2484 else if ( values.count() == 1 )
2485 layer = values.at( 0 );
2486 else
2487 {
2488 parent->setEvalErrorString( QObject::tr( "Function `num_selected` requires no more than one parameter. %n given.", nullptr, values.length() ) );
2489 return QVariant();
2490 }
2491
2492 bool foundLayer = false;
2493 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( layer, context, parent, []( QgsMapLayer * mapLayer ) -> QVariant
2494 {
2495 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( mapLayer );
2496 if ( !layer )
2497 {
2498 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2499 }
2500
2501 return layer->selectedFeatureCount();
2502 }, foundLayer );
2503 if ( !foundLayer )
2504 return QgsVariantUtils::createNullVariant( QMetaType::Type::LongLong );
2505 else
2506 return res;
2507}
2508
2509static QVariant fcnSqliteFetchAndIncrement( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
2510{
2511 static QMap<QString, qlonglong> counterCache;
2512 QVariant functionResult;
2513
2514 auto fetchAndIncrementFunc = [ values, parent, &functionResult ]( QgsMapLayer * mapLayer, const QString & databaseArgument )
2515 {
2516 QString database;
2517
2518 const QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( mapLayer );
2519
2520 if ( layer )
2521 {
2522 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
2523 database = decodedUri.value( QStringLiteral( "path" ) ).toString();
2524 if ( database.isEmpty() )
2525 {
2526 parent->setEvalErrorString( QObject::tr( "Could not extract file path from layer `%1`." ).arg( layer->name() ) );
2527 }
2528 }
2529 else
2530 {
2531 database = databaseArgument;
2532 }
2533
2534 const QString table = values.at( 1 ).toString();
2535 const QString idColumn = values.at( 2 ).toString();
2536 const QString filterAttribute = values.at( 3 ).toString();
2537 const QVariant filterValue = values.at( 4 ).toString();
2538 const QVariantMap defaultValues = values.at( 5 ).toMap();
2539
2540 // read from database
2542 sqlite3_statement_unique_ptr sqliteStatement;
2543
2544 if ( sqliteDb.open_v2( database, SQLITE_OPEN_READWRITE, nullptr ) != SQLITE_OK )
2545 {
2546 parent->setEvalErrorString( QObject::tr( "Could not open sqlite database %1. Error %2. " ).arg( database, sqliteDb.errorMessage() ) );
2547 functionResult = QVariant();
2548 return;
2549 }
2550
2551 QString errorMessage;
2552 QString currentValSql;
2553
2554 qlonglong nextId = 0;
2555 bool cachedMode = false;
2556 bool valueRetrieved = false;
2557
2558 QString cacheString = QStringLiteral( "%1:%2:%3:%4:%5" ).arg( database, table, idColumn, filterAttribute, filterValue.toString() );
2559
2560 // Running in transaction mode, check for cached value first
2561 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2562 {
2563 cachedMode = true;
2564
2565 auto cachedCounter = counterCache.find( cacheString );
2566
2567 if ( cachedCounter != counterCache.end() )
2568 {
2569 qlonglong &cachedValue = cachedCounter.value();
2570 nextId = cachedValue;
2571 nextId += 1;
2572 cachedValue = nextId;
2573 valueRetrieved = true;
2574 }
2575 }
2576
2577 // Either not in cached mode or no cached value found, obtain from DB
2578 if ( !cachedMode || !valueRetrieved )
2579 {
2580 int result = SQLITE_ERROR;
2581
2582 currentValSql = QStringLiteral( "SELECT %1 FROM %2" ).arg( QgsSqliteUtils::quotedIdentifier( idColumn ), QgsSqliteUtils::quotedIdentifier( table ) );
2583 if ( !filterAttribute.isNull() )
2584 {
2585 currentValSql += QStringLiteral( " WHERE %1 = %2" ).arg( QgsSqliteUtils::quotedIdentifier( filterAttribute ), QgsSqliteUtils::quotedValue( filterValue ) );
2586 }
2587
2588 sqliteStatement = sqliteDb.prepare( currentValSql, result );
2589
2590 if ( result == SQLITE_OK )
2591 {
2592 nextId = 0;
2593 if ( sqliteStatement.step() == SQLITE_ROW )
2594 {
2595 nextId = sqliteStatement.columnAsInt64( 0 ) + 1;
2596 }
2597
2598 // If in cached mode: add value to cache and connect to transaction
2599 if ( cachedMode && result == SQLITE_OK )
2600 {
2601 counterCache.insert( cacheString, nextId );
2602
2603 QObject::connect( layer->dataProvider()->transaction(), &QgsTransaction::destroyed, [cacheString]()
2604 {
2605 counterCache.remove( cacheString );
2606 } );
2607 }
2608 valueRetrieved = true;
2609 }
2610 }
2611
2612 if ( valueRetrieved )
2613 {
2614 QString upsertSql;
2615 upsertSql = QStringLiteral( "INSERT OR REPLACE INTO %1" ).arg( QgsSqliteUtils::quotedIdentifier( table ) );
2616 QStringList cols;
2617 QStringList vals;
2618 cols << QgsSqliteUtils::quotedIdentifier( idColumn );
2619 vals << QgsSqliteUtils::quotedValue( nextId );
2620
2621 if ( !filterAttribute.isNull() )
2622 {
2623 cols << QgsSqliteUtils::quotedIdentifier( filterAttribute );
2624 vals << QgsSqliteUtils::quotedValue( filterValue );
2625 }
2626
2627 for ( QVariantMap::const_iterator iter = defaultValues.constBegin(); iter != defaultValues.constEnd(); ++iter )
2628 {
2629 cols << QgsSqliteUtils::quotedIdentifier( iter.key() );
2630 vals << iter.value().toString();
2631 }
2632
2633 upsertSql += QLatin1String( " (" ) + cols.join( ',' ) + ')';
2634 upsertSql += QLatin1String( " VALUES " );
2635 upsertSql += '(' + vals.join( ',' ) + ')';
2636
2637 int result = SQLITE_ERROR;
2638 if ( layer && layer->dataProvider() && layer->dataProvider()->transaction() )
2639 {
2640 QgsTransaction *transaction = layer->dataProvider()->transaction();
2641 if ( transaction->executeSql( upsertSql, errorMessage ) )
2642 {
2643 result = SQLITE_OK;
2644 }
2645 }
2646 else
2647 {
2648 result = sqliteDb.exec( upsertSql, errorMessage );
2649 }
2650 if ( result == SQLITE_OK )
2651 {
2652 functionResult = QVariant( nextId );
2653 return;
2654 }
2655 else
2656 {
2657 parent->setEvalErrorString( QStringLiteral( "Could not increment value: SQLite error: \"%1\" (%2)." ).arg( errorMessage, QString::number( result ) ) );
2658 functionResult = QVariant();
2659 return;
2660 }
2661 }
2662
2663 functionResult = QVariant();
2664 };
2665
2666 bool foundLayer = false;
2667 QgsExpressionUtils::executeLambdaForMapLayer( values.at( 0 ), context, parent, [&fetchAndIncrementFunc]( QgsMapLayer * layer )
2668 {
2669 fetchAndIncrementFunc( layer, QString() );
2670 }, foundLayer );
2671 if ( !foundLayer )
2672 {
2673 const QString databasePath = values.at( 0 ).toString();
2674 QgsThreadingUtils::runOnMainThread( [&fetchAndIncrementFunc, databasePath]
2675 {
2676 fetchAndIncrementFunc( nullptr, databasePath );
2677 } );
2678 }
2679
2680 return functionResult;
2681}
2682
2683static QVariant fcnCrsToAuthid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2684{
2685 const QgsCoordinateReferenceSystem crs = QgsExpressionUtils::getCrsValue( values.at( 0 ), parent );
2686 if ( !crs.isValid() )
2687 return QVariant();
2688 return crs.authid();
2689}
2690
2691static QVariant fcnCrsFromText( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2692{
2693 QString definition = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2694 QgsCoordinateReferenceSystem crs( definition );
2695
2696 if ( !crs.isValid() )
2697 {
2698 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to coordinate reference system" ).arg( definition ) );
2699 }
2700
2701 return QVariant::fromValue( crs );
2702}
2703
2704static QVariant fcnConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2705{
2706 QString concat;
2707 for ( const QVariant &value : values )
2708 {
2709 if ( !QgsVariantUtils::isNull( value ) )
2710 concat += QgsExpressionUtils::getStringValue( value, parent );
2711 }
2712 return concat;
2713}
2714
2715static QVariant fcnStrpos( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2716{
2717 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2718 return string.indexOf( QgsExpressionUtils::getStringValue( values.at( 1 ), parent ) ) + 1;
2719}
2720
2721static QVariant fcnRight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2722{
2723 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2724 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2725 return string.right( pos );
2726}
2727
2728static QVariant fcnSubstrCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2729{
2730 if ( values.length() < 2 || values.length() > 3 )
2731 return QVariant();
2732
2733 const QString input = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2734 const QString substring = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2735
2736 bool overlapping = false;
2737 if ( values.length() == 3 )
2738 {
2739 overlapping = values.at( 2 ).toBool();
2740 }
2741
2742 if ( substring.isEmpty() )
2743 return QVariant( 0 );
2744
2745 int count = 0;
2746 if ( overlapping )
2747 {
2748 count = input.count( substring );
2749 }
2750 else
2751 {
2752 int pos = 0;
2753 while ( ( pos = input.indexOf( substring, pos ) ) != -1 )
2754 {
2755 count++;
2756 pos += substring.length();
2757 }
2758 }
2759
2760 return QVariant( count );
2761}
2762
2763static QVariant fcnLeft( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2764{
2765 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2766 int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2767 return string.left( pos );
2768}
2769
2770static QVariant fcnRPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2771{
2772 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2773 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2774 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2775 return string.leftJustified( length, fill.at( 0 ), true );
2776}
2777
2778static QVariant fcnLPad( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2779{
2780 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2781 int length = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
2782 QString fill = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2783 return string.rightJustified( length, fill.at( 0 ), true );
2784}
2785
2786static QVariant fcnFormatString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2787{
2788 if ( values.size() < 1 )
2789 {
2790 parent->setEvalErrorString( QObject::tr( "Function format requires at least 1 argument" ) );
2791 return QVariant();
2792 }
2793
2794 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2795 for ( int n = 1; n < values.length(); n++ )
2796 {
2797 string = string.arg( QgsExpressionUtils::getStringValue( values.at( n ), parent ) );
2798 }
2799 return string;
2800}
2801
2802
2803static QVariant fcnNow( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
2804{
2805 return QVariant( QDateTime::currentDateTime() );
2806}
2807
2808static QVariant fcnToDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2809{
2810 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2811 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2812 if ( format.isEmpty() && !language.isEmpty() )
2813 {
2814 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Date when the language is specified" ) );
2815 return QVariant( QDate() );
2816 }
2817
2818 if ( format.isEmpty() && language.isEmpty() )
2819 return QVariant( QgsExpressionUtils::getDateValue( values.at( 0 ), parent ) );
2820
2821 QString datestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2822 QLocale locale = QLocale();
2823 if ( !language.isEmpty() )
2824 {
2825 locale = QLocale( language );
2826 }
2827
2828 QDate date = locale.toDate( datestring, format );
2829 if ( !date.isValid() )
2830 {
2831 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Date" ).arg( datestring ) );
2832 date = QDate();
2833 }
2834 return QVariant( date );
2835}
2836
2837static QVariant fcnToTime( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2838{
2839 QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2840 QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
2841 if ( format.isEmpty() && !language.isEmpty() )
2842 {
2843 parent->setEvalErrorString( QObject::tr( "A format is required to convert to Time when the language is specified" ) );
2844 return QVariant( QTime() );
2845 }
2846
2847 if ( format.isEmpty() && language.isEmpty() )
2848 return QVariant( QgsExpressionUtils::getTimeValue( values.at( 0 ), parent ) );
2849
2850 QString timestring = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
2851 QLocale locale = QLocale();
2852 if ( !language.isEmpty() )
2853 {
2854 locale = QLocale( language );
2855 }
2856
2857 QTime time = locale.toTime( timestring, format );
2858 if ( !time.isValid() )
2859 {
2860 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to Time" ).arg( timestring ) );
2861 time = QTime();
2862 }
2863 return QVariant( time );
2864}
2865
2866static QVariant fcnToInterval( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2867{
2868 return QVariant::fromValue( QgsExpressionUtils::getInterval( values.at( 0 ), parent ) );
2869}
2870
2871/*
2872 * DMS functions
2873 */
2874
2875static QVariant floatToDegreeFormat( const QgsCoordinateFormatter::Format format, const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2876{
2877 double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2878 QString axis = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
2879 int precision = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
2880
2881 QString formatString;
2882 if ( values.count() > 3 )
2883 formatString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
2884
2886 if ( formatString.compare( QLatin1String( "suffix" ), Qt::CaseInsensitive ) == 0 )
2887 {
2889 }
2890 else if ( formatString.compare( QLatin1String( "aligned" ), Qt::CaseInsensitive ) == 0 )
2891 {
2893 }
2894 else if ( ! formatString.isEmpty() )
2895 {
2896 parent->setEvalErrorString( QObject::tr( "Invalid formatting parameter: '%1'. It must be empty, or 'suffix' or 'aligned'." ).arg( formatString ) );
2897 return QVariant();
2898 }
2899
2900 if ( axis.compare( QLatin1String( "x" ), Qt::CaseInsensitive ) == 0 )
2901 {
2902 return QVariant::fromValue( QgsCoordinateFormatter::formatX( value, format, precision, flags ) );
2903 }
2904 else if ( axis.compare( QLatin1String( "y" ), Qt::CaseInsensitive ) == 0 )
2905 {
2906 return QVariant::fromValue( QgsCoordinateFormatter::formatY( value, format, precision, flags ) );
2907 }
2908 else
2909 {
2910 parent->setEvalErrorString( QObject::tr( "Invalid axis name: '%1'. It must be either 'x' or 'y'." ).arg( axis ) );
2911 return QVariant();
2912 }
2913}
2914
2915static QVariant fcnToDegreeMinute( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2916{
2918 return floatToDegreeFormat( format, values, context, parent, node );
2919}
2920
2921static QVariant fcnToDecimal( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2922{
2923 double value = 0.0;
2924 bool ok = false;
2925 value = QgsCoordinateUtils::dmsToDecimal( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), &ok );
2926
2927 return ok ? QVariant( value ) : QVariant();
2928}
2929
2930static QVariant fcnToDegreeMinuteSecond( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
2931{
2933 return floatToDegreeFormat( format, values, context, parent, node );
2934}
2935
2936static QVariant fcnExtractDegrees( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2937{
2938 const double decimalDegrees = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
2939 return static_cast< int >( decimalDegrees );
2940}
2941
2942static QVariant fcnExtractMinutes( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2943{
2944 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2945 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2946 return static_cast< int >( remainder * 60 );
2947}
2948
2949static QVariant fcnExtractSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2950{
2951 const double absoluteDecimalDegrees = std::abs( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) );
2952 const double remainder = absoluteDecimalDegrees - static_cast<int>( absoluteDecimalDegrees );
2953 const double remainderInMinutes = remainder * 60;
2954 const double remainderSecondsFraction = remainderInMinutes - static_cast< int >( remainderInMinutes );
2955 // do not truncate to int, this function returns decimal seconds!
2956 return remainderSecondsFraction * 60;
2957}
2958
2959static QVariant fcnAge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2960{
2961 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
2962 QDateTime d2 = QgsExpressionUtils::getDateTimeValue( values.at( 1 ), parent );
2963 qint64 seconds = d2.secsTo( d1 );
2964 return QVariant::fromValue( QgsInterval( seconds ) );
2965}
2966
2967static QVariant fcnDayOfWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2968{
2969 if ( !values.at( 0 ).canConvert<QDate>() )
2970 return QVariant();
2971
2972 QDate date = QgsExpressionUtils::getDateValue( values.at( 0 ), parent );
2973 if ( !date.isValid() )
2974 return QVariant();
2975
2976 // return dayOfWeek() % 7 so that values range from 0 (sun) to 6 (sat)
2977 // (to match PostgreSQL behavior)
2978 return date.dayOfWeek() % 7;
2979}
2980
2981static QVariant fcnDay( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2982{
2983 QVariant value = values.at( 0 );
2984 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
2985 if ( inter.isValid() )
2986 {
2987 return QVariant( inter.days() );
2988 }
2989 else
2990 {
2991 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
2992 return QVariant( d1.date().day() );
2993 }
2994}
2995
2996static QVariant fcnYear( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
2997{
2998 QVariant value = values.at( 0 );
2999 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3000 if ( inter.isValid() )
3001 {
3002 return QVariant( inter.years() );
3003 }
3004 else
3005 {
3006 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3007 return QVariant( d1.date().year() );
3008 }
3009}
3010
3011static QVariant fcnMonth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3012{
3013 QVariant value = values.at( 0 );
3014 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3015 if ( inter.isValid() )
3016 {
3017 return QVariant( inter.months() );
3018 }
3019 else
3020 {
3021 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3022 return QVariant( d1.date().month() );
3023 }
3024}
3025
3026static QVariant fcnWeek( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3027{
3028 QVariant value = values.at( 0 );
3029 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3030 if ( inter.isValid() )
3031 {
3032 return QVariant( inter.weeks() );
3033 }
3034 else
3035 {
3036 QDateTime d1 = QgsExpressionUtils::getDateTimeValue( value, parent );
3037 return QVariant( d1.date().weekNumber() );
3038 }
3039}
3040
3041static QVariant fcnHour( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3042{
3043 QVariant value = values.at( 0 );
3044 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3045 if ( inter.isValid() )
3046 {
3047 return QVariant( inter.hours() );
3048 }
3049 else
3050 {
3051 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3052 return QVariant( t1.hour() );
3053 }
3054}
3055
3056static QVariant fcnMinute( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3057{
3058 QVariant value = values.at( 0 );
3059 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3060 if ( inter.isValid() )
3061 {
3062 return QVariant( inter.minutes() );
3063 }
3064 else
3065 {
3066 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3067 return QVariant( t1.minute() );
3068 }
3069}
3070
3071static QVariant fcnSeconds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3072{
3073 QVariant value = values.at( 0 );
3074 QgsInterval inter = QgsExpressionUtils::getInterval( value, parent, false );
3075 if ( inter.isValid() )
3076 {
3077 return QVariant( inter.seconds() );
3078 }
3079 else
3080 {
3081 QTime t1 = QgsExpressionUtils::getTimeValue( value, parent );
3082 return QVariant( t1.second() );
3083 }
3084}
3085
3086static QVariant fcnEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3087{
3088 QDateTime dt = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
3089 if ( dt.isValid() )
3090 {
3091 return QVariant( dt.toMSecsSinceEpoch() );
3092 }
3093 else
3094 {
3095 return QVariant();
3096 }
3097}
3098
3099static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3100{
3101 long long millisecs_since_epoch = QgsExpressionUtils::getIntValue( values.at( 0 ), parent );
3102 // no sense to check for strange values, as Qt behavior is undefined anyway (see docs)
3103 return QVariant( QDateTime::fromMSecsSinceEpoch( millisecs_since_epoch ) );
3104}
3105
3106static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3107{
3108 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3109 if ( parent->hasEvalError() )
3110 {
3111 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif" ) ) );
3112 return QVariant();
3113 }
3114 QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
3115 return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
3116}
3117
3118static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
3120 const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
3121 if ( parent->hasEvalError() )
3122 {
3123 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "exif_geotag" ) ) );
3124 return QVariant();
3125 }
3126 bool ok;
3127 return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
3128}
3129
3130#define ENSURE_GEOM_TYPE(f, g, geomtype) \
3131 if ( !(f).hasGeometry() ) \
3132 return QVariant(); \
3133 QgsGeometry g = (f).geometry(); \
3134 if ( (g).type() != (geomtype) ) \
3135 return QVariant();
3136
3137static QVariant fcnX( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3138{
3139 FEAT_FROM_CONTEXT( context, f )
3141 if ( g.isMultipart() )
3142 {
3143 return g.asMultiPoint().at( 0 ).x();
3144 }
3145 else
3146 {
3147 return g.asPoint().x();
3148 }
3149}
3150
3151static QVariant fcnY( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3152{
3153 FEAT_FROM_CONTEXT( context, f )
3155 if ( g.isMultipart() )
3156 {
3157 return g.asMultiPoint().at( 0 ).y();
3158 }
3159 else
3160 {
3161 return g.asPoint().y();
3162 }
3163}
3164
3165static QVariant fcnZ( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
3166{
3167 FEAT_FROM_CONTEXT( context, f )
3169
3170 if ( g.isEmpty() )
3171 return QVariant();
3172
3173 const QgsAbstractGeometry *abGeom = g.constGet();
3174
3175 if ( g.isEmpty() || !abGeom->is3D() )
3176 return QVariant();
3177
3178 if ( g.type() == Qgis::GeometryType::Point && !g.isMultipart() )
3179 {
3180 const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( g.constGet() );
3181 if ( point )
3182 return point->z();
3183 }
3184 else if ( g.type() == Qgis::GeometryType::Point && g.isMultipart() )
3185 {
3186 if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( g.constGet() ) )
3187 {
3188 if ( collection->numGeometries() > 0 )
3189 {
3190 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3191 return point->z();
3192 }
3193 }
3194 }
3195
3196 return QVariant();
3197}
3198
3199static QVariant fcnGeomIsValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3200{
3201 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3202 if ( geom.isNull() )
3203 return QVariant();
3204
3205 bool isValid = geom.isGeosValid();
3206
3207 return QVariant( isValid );
3208}
3209
3210static QVariant fcnGeomMakeValid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3211{
3212 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3213 if ( geom.isNull() )
3214 return QVariant();
3215
3216 const QString methodString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).trimmed();
3217#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
3219#else
3221#endif
3222 if ( methodString.compare( QLatin1String( "linework" ), Qt::CaseInsensitive ) == 0 )
3224 else if ( methodString.compare( QLatin1String( "structure" ), Qt::CaseInsensitive ) == 0 )
3226
3227 const bool keepCollapsed = values.value( 2 ).toBool();
3228
3229 QgsGeometry valid;
3230 try
3231 {
3232 valid = geom.makeValid( method, keepCollapsed );
3233 }
3234 catch ( QgsNotSupportedException & )
3235 {
3236 parent->setEvalErrorString( QObject::tr( "The make_valid parameters require a newer GEOS library version" ) );
3237 return QVariant();
3238 }
3239
3240 return QVariant::fromValue( valid );
3241}
3242
3243static QVariant fcnGeometryCollectionAsArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3244{
3245 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3246 if ( geom.isNull() )
3247 return QVariant();
3248
3249 QVector<QgsGeometry> multiGeom = geom.asGeometryCollection();
3250 QVariantList array;
3251 for ( int i = 0; i < multiGeom.size(); ++i )
3252 {
3253 array += QVariant::fromValue( multiGeom.at( i ) );
3254 }
3255
3256 return array;
3257}
3258
3259static QVariant fcnGeomX( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3260{
3261 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3262 if ( geom.isNull() )
3263 return QVariant();
3264
3265 //if single point, return the point's x coordinate
3266 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3267 {
3268 return geom.asPoint().x();
3269 }
3270
3271 //otherwise return centroid x
3272 QgsGeometry centroid = geom.centroid();
3273 QVariant result( centroid.asPoint().x() );
3274 return result;
3275}
3276
3277static QVariant fcnGeomY( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3278{
3279 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3280 if ( geom.isNull() )
3281 return QVariant();
3282
3283 //if single point, return the point's y coordinate
3284 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3285 {
3286 return geom.asPoint().y();
3287 }
3288
3289 //otherwise return centroid y
3290 QgsGeometry centroid = geom.centroid();
3291 QVariant result( centroid.asPoint().y() );
3292 return result;
3293}
3294
3295static QVariant fcnGeomZ( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3296{
3297 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3298 if ( geom.isNull() )
3299 return QVariant(); //or 0?
3300
3301 if ( !geom.constGet()->is3D() )
3302 return QVariant();
3303
3304 //if single point, return the point's z coordinate
3305 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3306 {
3308 if ( point )
3309 return point->z();
3310 }
3311 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3312 {
3314 {
3315 if ( collection->numGeometries() == 1 )
3316 {
3317 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3318 return point->z();
3319 }
3320 }
3321 }
3322
3323 return QVariant();
3324}
3325
3326static QVariant fcnGeomM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3327{
3328 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3329 if ( geom.isNull() )
3330 return QVariant(); //or 0?
3331
3332 if ( !geom.constGet()->isMeasure() )
3333 return QVariant();
3334
3335 //if single point, return the point's m value
3336 if ( geom.type() == Qgis::GeometryType::Point && !geom.isMultipart() )
3337 {
3339 if ( point )
3340 return point->m();
3341 }
3342 else if ( geom.type() == Qgis::GeometryType::Point && geom.isMultipart() )
3343 {
3345 {
3346 if ( collection->numGeometries() == 1 )
3347 {
3348 if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) ) )
3349 return point->m();
3350 }
3351 }
3352 }
3353
3354 return QVariant();
3355}
3356
3357static QVariant fcnPointN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3358{
3359 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3360
3361 if ( geom.isNull() )
3362 return QVariant();
3363
3364 int idx = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
3365
3366 if ( idx < 0 )
3367 {
3368 //negative idx
3369 int count = geom.constGet()->nCoordinates();
3370 idx = count + idx;
3371 }
3372 else
3373 {
3374 //positive idx is 1 based
3375 idx -= 1;
3376 }
3377
3378 QgsVertexId vId;
3379 if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
3380 {
3381 parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
3382 return QVariant();
3383 }
3384
3385 QgsPoint point = geom.constGet()->vertexAt( vId );
3386 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3387}
3388
3389static QVariant fcnStartPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3390{
3391 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3392
3393 if ( geom.isNull() )
3394 return QVariant();
3395
3396 QgsVertexId vId;
3397 if ( !geom.vertexIdFromVertexNr( 0, vId ) )
3398 {
3399 return QVariant();
3400 }
3401
3402 QgsPoint point = geom.constGet()->vertexAt( vId );
3403 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3404}
3405
3406static QVariant fcnEndPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3407{
3408 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3409
3410 if ( geom.isNull() )
3411 return QVariant();
3412
3413 QgsVertexId vId;
3414 if ( !geom.vertexIdFromVertexNr( geom.constGet()->nCoordinates() - 1, vId ) )
3415 {
3416 return QVariant();
3417 }
3418
3419 QgsPoint point = geom.constGet()->vertexAt( vId );
3420 return QVariant::fromValue( QgsGeometry( new QgsPoint( point ) ) );
3421}
3422
3423static QVariant fcnNodesToPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3424{
3425 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3426
3427 if ( geom.isNull() )
3428 return QVariant();
3429
3430 bool ignoreClosing = false;
3431 if ( values.length() > 1 )
3432 {
3433 ignoreClosing = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3434 }
3435
3436 QgsMultiPoint *mp = new QgsMultiPoint();
3437
3438 const QgsCoordinateSequence sequence = geom.constGet()->coordinateSequence();
3439 for ( const QgsRingSequence &part : sequence )
3440 {
3441 for ( const QgsPointSequence &ring : part )
3442 {
3443 bool skipLast = false;
3444 if ( ignoreClosing && ring.count() > 2 && ring.first() == ring.last() )
3445 {
3446 skipLast = true;
3447 }
3448
3449 for ( int i = 0; i < ( skipLast ? ring.count() - 1 : ring.count() ); ++ i )
3450 {
3451 mp->addGeometry( ring.at( i ).clone() );
3452 }
3453 }
3454 }
3455
3456 return QVariant::fromValue( QgsGeometry( mp ) );
3457}
3458
3459static QVariant fcnSegmentsToLines( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3460{
3461 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3462
3463 if ( geom.isNull() )
3464 return QVariant();
3465
3466 const QVector< QgsLineString * > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.constGet() );
3467
3468 //OK, now we have a complete list of segmentized lines from the geometry
3470 for ( QgsLineString *line : linesToProcess )
3471 {
3472 for ( int i = 0; i < line->numPoints() - 1; ++i )
3473 {
3475 segment->setPoints( QgsPointSequence()
3476 << line->pointN( i )
3477 << line->pointN( i + 1 ) );
3478 ml->addGeometry( segment );
3479 }
3480 delete line;
3481 }
3482
3483 return QVariant::fromValue( QgsGeometry( ml ) );
3484}
3485
3486static QVariant fcnInteriorRingN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3487{
3488 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3489
3490 if ( geom.isNull() )
3491 return QVariant();
3492
3494 if ( !curvePolygon && geom.isMultipart() )
3495 {
3497 {
3498 if ( collection->numGeometries() == 1 )
3499 {
3500 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
3501 }
3502 }
3503 }
3504
3505 if ( !curvePolygon )
3506 return QVariant();
3507
3508 //idx is 1 based
3509 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3510
3511 if ( idx >= curvePolygon->numInteriorRings() || idx < 0 )
3512 return QVariant();
3513
3514 QgsCurve *curve = static_cast< QgsCurve * >( curvePolygon->interiorRing( static_cast< int >( idx ) )->clone() );
3515 QVariant result = curve ? QVariant::fromValue( QgsGeometry( curve ) ) : QVariant();
3516 return result;
3517}
3518
3519static QVariant fcnGeometryN( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3520{
3521 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3522
3523 if ( geom.isNull() )
3524 return QVariant();
3525
3527 if ( !collection )
3528 return QVariant();
3529
3530 //idx is 1 based
3531 qlonglong idx = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) - 1;
3532
3533 if ( idx < 0 || idx >= collection->numGeometries() )
3534 return QVariant();
3535
3536 QgsAbstractGeometry *part = collection->geometryN( static_cast< int >( idx ) )->clone();
3537 QVariant result = part ? QVariant::fromValue( QgsGeometry( part ) ) : QVariant();
3538 return result;
3539}
3540
3541static QVariant fcnBoundary( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3542{
3543 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3544
3545 if ( geom.isNull() )
3546 return QVariant();
3547
3548 QgsAbstractGeometry *boundary = geom.constGet()->boundary();
3549 if ( !boundary )
3550 return QVariant();
3551
3552 return QVariant::fromValue( QgsGeometry( boundary ) );
3553}
3554
3555static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3556{
3557 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3558
3559 if ( geom.isNull() )
3560 return QVariant();
3561
3562 QgsGeometry merged = geom.mergeLines();
3563 if ( merged.isNull() )
3564 return QVariant();
3565
3566 return QVariant::fromValue( merged );
3567}
3568
3569static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3570{
3571 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3572 if ( geom.isNull() )
3573 return QVariant();
3574
3575 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
3576 if ( geom2.isNull() )
3577 return QVariant();
3578
3579 const QgsGeometry sharedPaths = geom.sharedPaths( geom2 );
3580 if ( sharedPaths.isNull() )
3581 return QVariant();
3582
3583 return QVariant::fromValue( sharedPaths );
3584}
3585
3586
3587static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3588{
3589 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3590
3591 if ( geom.isNull() )
3592 return QVariant();
3593
3594 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3595
3596 QgsGeometry simplified = geom.simplify( tolerance );
3597 if ( simplified.isNull() )
3598 return QVariant();
3599
3600 return simplified;
3601}
3602
3603static QVariant fcnSimplifyVW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3604{
3605 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3606
3607 if ( geom.isNull() )
3608 return QVariant();
3609
3610 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3611
3613
3614 QgsGeometry simplified = simplifier.simplify( geom );
3615 if ( simplified.isNull() )
3616 return QVariant();
3617
3618 return simplified;
3619}
3620
3621static QVariant fcnSmooth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3622{
3623 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3624
3625 if ( geom.isNull() )
3626 return QVariant();
3627
3628 int iterations = std::min( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), 10 );
3629 double offset = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0.0, 0.5 );
3630 double minLength = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3631 double maxAngle = std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ), 0.0, 180.0 );
3632
3633 QgsGeometry smoothed = geom.smooth( static_cast<unsigned int>( iterations ), offset, minLength, maxAngle );
3634 if ( smoothed.isNull() )
3635 return QVariant();
3636
3637 return smoothed;
3638}
3639
3640static QVariant fcnTriangularWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3641{
3642 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3643
3644 if ( geom.isNull() )
3645 return QVariant();
3646
3647 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3648 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3649 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3650
3651 const QgsGeometry waved = geom.triangularWaves( wavelength, amplitude, strict );
3652 if ( waved.isNull() )
3653 return QVariant();
3654
3655 return waved;
3656}
3657
3658static QVariant fcnTriangularWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3659{
3660 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3661
3662 if ( geom.isNull() )
3663 return QVariant();
3664
3665 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3666 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3667 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3668 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3669 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3670
3671 const QgsGeometry waved = geom.triangularWavesRandomized( minWavelength, maxWavelength,
3672 minAmplitude, maxAmplitude, seed );
3673 if ( waved.isNull() )
3674 return QVariant();
3675
3676 return waved;
3677}
3678
3679static QVariant fcnSquareWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3680{
3681 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3682
3683 if ( geom.isNull() )
3684 return QVariant();
3685
3686 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3687 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3688 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3689
3690 const QgsGeometry waved = geom.squareWaves( wavelength, amplitude, strict );
3691 if ( waved.isNull() )
3692 return QVariant();
3693
3694 return waved;
3695}
3696
3697static QVariant fcnSquareWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3698{
3699 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3700
3701 if ( geom.isNull() )
3702 return QVariant();
3703
3704 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3705 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3706 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3707 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3708 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3709
3710 const QgsGeometry waved = geom.squareWavesRandomized( minWavelength, maxWavelength,
3711 minAmplitude, maxAmplitude, seed );
3712 if ( waved.isNull() )
3713 return QVariant();
3714
3715 return waved;
3716}
3717
3718static QVariant fcnRoundWave( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3719{
3720 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3721
3722 if ( geom.isNull() )
3723 return QVariant();
3724
3725 const double wavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3726 const double amplitude = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3727 const bool strict = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
3728
3729 const QgsGeometry waved = geom.roundWaves( wavelength, amplitude, strict );
3730 if ( waved.isNull() )
3731 return QVariant();
3732
3733 return waved;
3734}
3735
3736static QVariant fcnRoundWaveRandomized( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3737{
3738 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3739
3740 if ( geom.isNull() )
3741 return QVariant();
3742
3743 const double minWavelength = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3744 const double maxWavelength = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3745 const double minAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
3746 const double maxAmplitude = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
3747 const long long seed = QgsExpressionUtils::getIntValue( values.at( 5 ), parent );
3748
3749 const QgsGeometry waved = geom.roundWavesRandomized( minWavelength, maxWavelength,
3750 minAmplitude, maxAmplitude, seed );
3751 if ( waved.isNull() )
3752 return QVariant();
3753
3754 return waved;
3755}
3756
3757static QVariant fcnApplyDashPattern( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3758{
3759 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3760
3761 if ( geom.isNull() )
3762 return QVariant();
3763
3764 const QVariantList pattern = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
3765 QVector< double > dashPattern;
3766 dashPattern.reserve( pattern.size() );
3767 for ( const QVariant &value : std::as_const( pattern ) )
3768 {
3769 bool ok = false;
3770 double v = value.toDouble( &ok );
3771 if ( ok )
3772 {
3773 dashPattern << v;
3774 }
3775 else
3776 {
3777 parent->setEvalErrorString( QStringLiteral( "Dash pattern must be an array of numbers" ) );
3778 return QgsGeometry();
3779 }
3780 }
3781
3782 if ( dashPattern.size() % 2 != 0 )
3783 {
3784 parent->setEvalErrorString( QStringLiteral( "Dash pattern must contain an even number of elements" ) );
3785 return QgsGeometry();
3786 }
3787
3788 const QString startRuleString = QgsExpressionUtils::getStringValue( values.at( 2 ), parent ).trimmed();
3790 if ( startRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3792 else if ( startRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3794 else if ( startRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3796 else if ( startRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3798 else if ( startRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3800 else
3801 {
3802 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( startRuleString ) );
3803 return QgsGeometry();
3804 }
3805
3806 const QString endRuleString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
3808 if ( endRuleString.compare( QLatin1String( "no_rule" ), Qt::CaseInsensitive ) == 0 )
3810 else if ( endRuleString.compare( QLatin1String( "full_dash" ), Qt::CaseInsensitive ) == 0 )
3812 else if ( endRuleString.compare( QLatin1String( "half_dash" ), Qt::CaseInsensitive ) == 0 )
3814 else if ( endRuleString.compare( QLatin1String( "full_gap" ), Qt::CaseInsensitive ) == 0 )
3816 else if ( endRuleString.compare( QLatin1String( "half_gap" ), Qt::CaseInsensitive ) == 0 )
3818 else
3819 {
3820 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern rule" ).arg( endRuleString ) );
3821 return QgsGeometry();
3822 }
3823
3824 const QString adjustString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
3826 if ( adjustString.compare( QLatin1String( "both" ), Qt::CaseInsensitive ) == 0 )
3828 else if ( adjustString.compare( QLatin1String( "dash" ), Qt::CaseInsensitive ) == 0 )
3830 else if ( adjustString.compare( QLatin1String( "gap" ), Qt::CaseInsensitive ) == 0 )
3832 else
3833 {
3834 parent->setEvalErrorString( QStringLiteral( "'%1' is not a valid dash pattern size adjustment" ).arg( adjustString ) );
3835 return QgsGeometry();
3836 }
3837
3838 const double patternOffset = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
3839
3840 const QgsGeometry result = geom.applyDashPattern( dashPattern, startRule, endRule, adjustment, patternOffset );
3841 if ( result.isNull() )
3842 return QVariant();
3843
3844 return result;
3845}
3846
3847static QVariant fcnDensifyByCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3848{
3849 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3850
3851 if ( geom.isNull() )
3852 return QVariant();
3853
3854 const long long count = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
3855 const QgsGeometry densified = geom.densifyByCount( static_cast< int >( count ) );
3856 if ( densified.isNull() )
3857 return QVariant();
3858
3859 return densified;
3860}
3861
3862static QVariant fcnDensifyByDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3863{
3864 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3865
3866 if ( geom.isNull() )
3867 return QVariant();
3868
3869 const double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3870 const QgsGeometry densified = geom.densifyByDistance( distance );
3871 if ( densified.isNull() )
3872 return QVariant();
3873
3874 return densified;
3875}
3876
3877static QVariant fcnCollectGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3878{
3879 QVariantList list;
3880 if ( values.size() == 1 && QgsExpressionUtils::isList( values.at( 0 ) ) )
3881 {
3882 list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
3883 }
3884 else
3885 {
3886 list = values;
3887 }
3888
3889 QVector< QgsGeometry > parts;
3890 parts.reserve( list.size() );
3891 for ( const QVariant &value : std::as_const( list ) )
3892 {
3893 QgsGeometry part = QgsExpressionUtils::getGeometry( value, parent );
3894 if ( part.isNull() )
3895 return QgsGeometry();
3896 parts << part;
3897 }
3898
3899 return QgsGeometry::collectGeometry( parts );
3900}
3901
3902static QVariant fcnMakePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3903{
3904 if ( values.count() < 2 || values.count() > 4 )
3905 {
3906 parent->setEvalErrorString( QObject::tr( "Function make_point requires 2-4 arguments" ) );
3907 return QVariant();
3908 }
3909
3910 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3911 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3912 double z = values.count() >= 3 ? QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) : 0.0;
3913 double m = values.count() >= 4 ? QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) : 0.0;
3914 switch ( values.count() )
3915 {
3916 case 2:
3917 return QVariant::fromValue( QgsGeometry( new QgsPoint( x, y ) ) );
3918 case 3:
3919 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZ, x, y, z ) ) );
3920 case 4:
3921 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointZM, x, y, z, m ) ) );
3922 }
3923 return QVariant(); //avoid warning
3924}
3925
3926static QVariant fcnMakePointM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3927{
3928 double x = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
3929 double y = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
3930 double m = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
3931 return QVariant::fromValue( QgsGeometry( new QgsPoint( Qgis::WkbType::PointM, x, y, 0.0, m ) ) );
3932}
3933
3934static QVariant fcnMakeLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3935{
3936 if ( values.empty() )
3937 {
3938 return QVariant();
3939 }
3940
3941 QVector<QgsPoint> points;
3942 points.reserve( values.count() );
3943
3944 auto addPoint = [&points]( const QgsGeometry & geom )
3945 {
3946 if ( geom.isNull() )
3947 return;
3948
3949 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
3950 return;
3951
3953 if ( !point )
3954 return;
3955
3956 points << *point;
3957 };
3958
3959 for ( const QVariant &value : values )
3960 {
3961 if ( value.userType() == QMetaType::Type::QVariantList )
3962 {
3963 const QVariantList list = value.toList();
3964 for ( const QVariant &v : list )
3965 {
3966 addPoint( QgsExpressionUtils::getGeometry( v, parent ) );
3967 }
3968 }
3969 else
3970 {
3971 addPoint( QgsExpressionUtils::getGeometry( value, parent ) );
3972 }
3973 }
3974
3975 if ( points.count() < 2 )
3976 return QVariant();
3977
3978 return QgsGeometry( new QgsLineString( points ) );
3979}
3980
3981static QVariant fcnMakePolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
3982{
3983 if ( values.count() < 1 )
3984 {
3985 parent->setEvalErrorString( QObject::tr( "Function make_polygon requires an argument" ) );
3986 return QVariant();
3987 }
3988
3989 QgsGeometry outerRing = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
3990
3991 if ( outerRing.type() == Qgis::GeometryType::Polygon )
3992 return outerRing; // if it's already a polygon we have nothing to do
3993
3994 if ( outerRing.type() != Qgis::GeometryType::Line || outerRing.isNull() )
3995 return QVariant();
3996
3997 auto polygon = std::make_unique< QgsPolygon >();
3998
3999 const QgsCurve *exteriorRing = qgsgeometry_cast< const QgsCurve * >( outerRing.constGet() );
4000 if ( !exteriorRing && outerRing.isMultipart() )
4001 {
4003 {
4004 if ( collection->numGeometries() == 1 )
4005 {
4006 exteriorRing = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4007 }
4008 }
4009 }
4010
4011 if ( !exteriorRing )
4012 return QVariant();
4013
4014 polygon->setExteriorRing( exteriorRing->segmentize() );
4015
4016
4017 for ( int i = 1; i < values.count(); ++i )
4018 {
4019 QgsGeometry ringGeom = QgsExpressionUtils::getGeometry( values.at( i ), parent );
4020 if ( ringGeom.isNull() )
4021 continue;
4022
4023 if ( ringGeom.type() != Qgis::GeometryType::Line || ringGeom.isNull() )
4024 continue;
4025
4026 const QgsCurve *ring = qgsgeometry_cast< const QgsCurve * >( ringGeom.constGet() );
4027 if ( !ring && ringGeom.isMultipart() )
4028 {
4030 {
4031 if ( collection->numGeometries() == 1 )
4032 {
4033 ring = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4034 }
4035 }
4036 }
4037
4038 if ( !ring )
4039 continue;
4040
4041 polygon->addInteriorRing( ring->segmentize() );
4042 }
4043
4044 return QVariant::fromValue( QgsGeometry( std::move( polygon ) ) );
4045}
4046
4047static QVariant fcnMakeTriangle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4048{
4049 auto tr = std::make_unique<QgsTriangle>();
4050 auto lineString = std::make_unique<QgsLineString>();
4051 lineString->clear();
4052
4053 for ( const QVariant &value : values )
4054 {
4055 QgsGeometry geom = QgsExpressionUtils::getGeometry( value, parent );
4056 if ( geom.isNull() )
4057 return QVariant();
4058
4059 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4060 return QVariant();
4061
4063 if ( !point && geom.isMultipart() )
4064 {
4066 {
4067 if ( collection->numGeometries() == 1 )
4068 {
4069 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4070 }
4071 }
4072 }
4073
4074 if ( !point )
4075 return QVariant();
4076
4077 lineString->addVertex( *point );
4078 }
4079
4080 tr->setExteriorRing( lineString.release() );
4081
4082 return QVariant::fromValue( QgsGeometry( tr.release() ) );
4083}
4084
4085static QVariant fcnMakeCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4086{
4087 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4088 if ( geom.isNull() )
4089 return QVariant();
4090
4091 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4092 return QVariant();
4093
4094 double radius = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4095 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
4096
4097 if ( segment < 3 )
4098 {
4099 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4100 return QVariant();
4101 }
4103 if ( !point && geom.isMultipart() )
4104 {
4106 {
4107 if ( collection->numGeometries() == 1 )
4108 {
4109 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4110 }
4111 }
4112 }
4113 if ( !point )
4114 return QVariant();
4115
4116 QgsCircle circ( *point, radius );
4117 return QVariant::fromValue( QgsGeometry( circ.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4118}
4119
4120static QVariant fcnMakeEllipse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4121{
4122 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4123 if ( geom.isNull() )
4124 return QVariant();
4125
4126 if ( geom.type() != Qgis::GeometryType::Point || geom.isMultipart() )
4127 return QVariant();
4128
4129 double majorAxis = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
4130 double minorAxis = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
4131 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
4132 int segment = QgsExpressionUtils::getNativeIntValue( values.at( 4 ), parent );
4133 if ( segment < 3 )
4134 {
4135 parent->setEvalErrorString( QObject::tr( "Segment must be greater than 2" ) );
4136 return QVariant();
4137 }
4139 if ( !point && geom.isMultipart() )
4140 {
4142 {
4143 if ( collection->numGeometries() == 1 )
4144 {
4145 point = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4146 }
4147 }
4148 }
4149 if ( !point )
4150 return QVariant();
4151
4152 QgsEllipse elp( *point, majorAxis, minorAxis, azimuth );
4153 return QVariant::fromValue( QgsGeometry( elp.toPolygon( static_cast<unsigned int>( segment ) ) ) );
4154}
4155
4156static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4157{
4158
4159 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4160 if ( pt1.isNull() )
4161 return QVariant();
4162
4163 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4164 return QVariant();
4165
4166 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4167 if ( pt2.isNull() )
4168 return QVariant();
4169
4170 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4171 return QVariant();
4172
4173 unsigned int nbEdges = static_cast<unsigned int>( QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) );
4174 if ( nbEdges < 3 )
4175 {
4176 parent->setEvalErrorString( QObject::tr( "Number of edges/sides must be greater than 2" ) );
4177 return QVariant();
4178 }
4179
4180 QgsRegularPolygon::ConstructionOption option = static_cast< QgsRegularPolygon::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4182 {
4183 parent->setEvalErrorString( QObject::tr( "Option can be 0 (inscribed) or 1 (circumscribed)" ) );
4184 return QVariant();
4185 }
4186
4188 if ( !center && pt1.isMultipart() )
4189 {
4191 {
4192 if ( collection->numGeometries() == 1 )
4193 {
4194 center = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4195 }
4196 }
4197 }
4198 if ( !center )
4199 return QVariant();
4200
4202 if ( !corner && pt2.isMultipart() )
4203 {
4205 {
4206 if ( collection->numGeometries() == 1 )
4207 {
4208 corner = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
4209 }
4210 }
4211 }
4212 if ( !corner )
4213 return QVariant();
4214
4215 QgsRegularPolygon rp = QgsRegularPolygon( *center, *corner, nbEdges, option );
4216
4217 return QVariant::fromValue( QgsGeometry( rp.toPolygon() ) );
4218
4219}
4220
4221static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4222{
4223 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4224 if ( pt1.isNull() )
4225 return QVariant();
4226 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4227 return QVariant();
4228
4229 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4230 if ( pt2.isNull() )
4231 return QVariant();
4232 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4233 return QVariant();
4234
4235 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4236 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4237 QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );
4238
4239 return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
4240}
4241
4242static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4243{
4244 QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4245 if ( pt1.isNull() )
4246 return QVariant();
4247 if ( pt1.type() != Qgis::GeometryType::Point || pt1.isMultipart() )
4248 return QVariant();
4249
4250 QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4251 if ( pt2.isNull() )
4252 return QVariant();
4253 if ( pt2.type() != Qgis::GeometryType::Point || pt2.isMultipart() )
4254 return QVariant();
4255
4256 QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
4257 if ( pt3.isNull() )
4258 return QVariant();
4259 if ( pt3.type() != Qgis::GeometryType::Point || pt3.isMultipart() )
4260 return QVariant();
4261
4262 QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
4263 if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
4264 {
4265 parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
4266 return QVariant();
4267 }
4268 const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
4269 const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
4270 const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
4271 QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
4272 return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
4273}
4274
4275static QVariant pointAt( const QgsGeometry &geom, int idx, QgsExpression *parent ) // helper function
4276{
4277 if ( geom.isNull() )
4278 return QVariant();
4279
4280 if ( idx < 0 )
4281 {
4282 idx += geom.constGet()->nCoordinates();
4283 }
4284 if ( idx < 0 || idx >= geom.constGet()->nCoordinates() )
4285 {
4286 parent->setEvalErrorString( QObject::tr( "Index is out of range" ) );
4287 return QVariant();
4288 }
4289 return QVariant::fromValue( geom.vertexAt( idx ) );
4290}
4291
4292// function used for the old $ style
4293static QVariant fcnOldXat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4294{
4295 FEAT_FROM_CONTEXT( context, feature )
4296 const QgsGeometry geom = feature.geometry();
4297 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4298
4299 const QVariant v = pointAt( geom, idx, parent );
4300
4301 if ( !v.isNull() )
4302 return QVariant( v.value<QgsPoint>().x() );
4303 else
4304 return QVariant();
4305}
4306static QVariant fcnXat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4307{
4308 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias x_at function is called like a $ function (x_at(i))
4309 {
4310 return fcnOldXat( values, f, parent, node );
4311 }
4312 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4313 {
4314 return fcnOldXat( QVariantList() << values[1], f, parent, node );
4315 }
4316
4317 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4318 if ( geom.isNull() )
4319 {
4320 return QVariant();
4321 }
4322
4323 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4324
4325 const QVariant v = pointAt( geom, vertexNumber, parent );
4326 if ( !v.isNull() )
4327 return QVariant( v.value<QgsPoint>().x() );
4328 else
4329 return QVariant();
4330}
4331
4332// function used for the old $ style
4333static QVariant fcnOldYat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4334{
4335 FEAT_FROM_CONTEXT( context, feature )
4336 const QgsGeometry geom = feature.geometry();
4337 const int idx = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
4338
4339 const QVariant v = pointAt( geom, idx, parent );
4340
4341 if ( !v.isNull() )
4342 return QVariant( v.value<QgsPoint>().y() );
4343 else
4344 return QVariant();
4345}
4346static QVariant fcnYat( const QVariantList &values, const QgsExpressionContext *f, QgsExpression *parent, const QgsExpressionNodeFunction *node )
4347{
4348 if ( values.at( 1 ).isNull() && !values.at( 0 ).isNull() ) // the case where the alias y_at function is called like a $ function (y_at(i))
4349 {
4350 return fcnOldYat( values, f, parent, node );
4351 }
4352 else if ( values.at( 0 ).isNull() && !values.at( 1 ).isNull() ) // same as above with x_at(i:=0) (vertex value is at the second position)
4353 {
4354 return fcnOldYat( QVariantList() << values[1], f, parent, node );
4355 }
4356
4357 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4358 if ( geom.isNull() )
4359 {
4360 return QVariant();
4361 }
4362
4363 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4364
4365 const QVariant v = pointAt( geom, vertexNumber, parent );
4366 if ( !v.isNull() )
4367 return QVariant( v.value<QgsPoint>().y() );
4368 else
4369 return QVariant();
4370}
4371
4372static QVariant fcnZat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4373{
4374 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4375 if ( geom.isNull() )
4376 {
4377 return QVariant();
4378 }
4379
4380 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4381
4382 const QVariant v = pointAt( geom, vertexNumber, parent );
4383 if ( !v.isNull() && v.value<QgsPoint>().is3D() )
4384 return QVariant( v.value<QgsPoint>().z() );
4385 else
4386 return QVariant();
4387}
4388
4389static QVariant fcnMat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4390{
4391 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4392 if ( geom.isNull() )
4393 {
4394 return QVariant();
4395 }
4396
4397 const int vertexNumber = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
4398
4399 const QVariant v = pointAt( geom, vertexNumber, parent );
4400 if ( !v.isNull() && v.value<QgsPoint>().isMeasure() )
4401 return QVariant( v.value<QgsPoint>().m() );
4402 else
4403 return QVariant();
4404}
4405
4406
4407static QVariant fcnGeometry( const QVariantList &, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * )
4408{
4409 if ( !context )
4410 return QVariant();
4411
4412 // prefer geometry from context if it's present, otherwise fallback to context's feature's geometry
4413 if ( context->hasGeometry() )
4414 return context->geometry();
4415 else
4416 {
4417 FEAT_FROM_CONTEXT( context, f )
4418 QgsGeometry geom = f.geometry();
4419 if ( !geom.isNull() )
4420 return QVariant::fromValue( geom );
4421 else
4422 return QVariant();
4423 }
4424}
4425
4426static QVariant fcnGeomFromWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4427{
4428 QString wkt = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4429 QgsGeometry geom = QgsGeometry::fromWkt( wkt );
4430 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4431 return result;
4432}
4433
4434static QVariant fcnGeomFromWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4435{
4436 const QByteArray wkb = QgsExpressionUtils::getBinaryValue( values.at( 0 ), parent );
4437 if ( wkb.isNull() )
4438 return QVariant();
4439
4440 QgsGeometry geom;
4441 geom.fromWkb( wkb );
4442 return !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4443}
4444
4445static QVariant fcnGeomFromGML( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4446{
4447 QString gml = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
4448 QgsOgcUtils::Context ogcContext;
4449 if ( context )
4450 {
4451 QgsWeakMapLayerPointer mapLayerPtr {context->variable( QStringLiteral( "layer" ) ).value<QgsWeakMapLayerPointer>() };
4452 if ( mapLayerPtr )
4453 {
4454 ogcContext.layer = mapLayerPtr.data();
4455 ogcContext.transformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
4456 }
4457 }
4458 QgsGeometry geom = QgsOgcUtils::geometryFromGML( gml, ogcContext );
4459 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
4460 return result;
4461}
4462
4463static QVariant fcnGeomArea( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4464{
4465 FEAT_FROM_CONTEXT( context, f )
4467 QgsDistanceArea *calc = parent->geomCalculator();
4468 if ( calc )
4469 {
4470 try
4471 {
4472 double area = calc->measureArea( f.geometry() );
4473 area = calc->convertAreaMeasurement( area, parent->areaUnits() );
4474 return QVariant( area );
4475 }
4476 catch ( QgsCsException & )
4477 {
4478 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating area" ) );
4479 return QVariant();
4480 }
4481 }
4482 else
4483 {
4484 return QVariant( f.geometry().area() );
4485 }
4486}
4487
4488static QVariant fcnArea( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4489{
4490 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4491
4492 if ( geom.type() != Qgis::GeometryType::Polygon )
4493 return QVariant();
4494
4495 return QVariant( geom.area() );
4496}
4497
4498static QVariant fcnGeomLength( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4499{
4500 FEAT_FROM_CONTEXT( context, f )
4502 QgsDistanceArea *calc = parent->geomCalculator();
4503 if ( calc )
4504 {
4505 try
4506 {
4507 double len = calc->measureLength( f.geometry() );
4508 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4509 return QVariant( len );
4510 }
4511 catch ( QgsCsException & )
4512 {
4513 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating length" ) );
4514 return QVariant();
4515 }
4516 }
4517 else
4518 {
4519 return QVariant( f.geometry().length() );
4520 }
4521}
4522
4523static QVariant fcnGeomPerimeter( const QVariantList &, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
4524{
4525 FEAT_FROM_CONTEXT( context, f )
4527 QgsDistanceArea *calc = parent->geomCalculator();
4528 if ( calc )
4529 {
4530 try
4531 {
4532 double len = calc->measurePerimeter( f.geometry() );
4533 len = calc->convertLengthMeasurement( len, parent->distanceUnits() );
4534 return QVariant( len );
4535 }
4536 catch ( QgsCsException & )
4537 {
4538 parent->setEvalErrorString( QObject::tr( "An error occurred while calculating perimeter" ) );
4539 return QVariant();
4540 }
4541 }
4542 else
4543 {
4544 return f.geometry().isNull() ? QVariant( 0 ) : QVariant( f.geometry().constGet()->perimeter() );
4545 }
4546}
4547
4548static QVariant fcnPerimeter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4549{
4550 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4551
4552 if ( geom.type() != Qgis::GeometryType::Polygon )
4553 return QVariant();
4554
4555 //length for polygons = perimeter
4556 return QVariant( geom.length() );
4557}
4558
4559static QVariant fcnGeomNumPoints( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4560{
4561 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4562 return QVariant( geom.isNull() ? 0 : geom.constGet()->nCoordinates() );
4563}
4564
4565static QVariant fcnGeomNumGeometries( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4566{
4567 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4568 if ( geom.isNull() )
4569 return QVariant();
4570
4571 return QVariant( geom.constGet()->partCount() );
4572}
4573
4574static QVariant fcnGeomIsMultipart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4575{
4576 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4577 if ( geom.isNull() )
4578 return QVariant();
4579
4580 return QVariant( geom.isMultipart() );
4581}
4582
4583static QVariant fcnGeomNumInteriorRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4584{
4585 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4586
4587 if ( geom.isNull() )
4588 return QVariant();
4589
4591 if ( curvePolygon )
4592 return QVariant( curvePolygon->numInteriorRings() );
4593
4595 if ( collection )
4596 {
4597 //find first CurvePolygon in collection
4598 for ( int i = 0; i < collection->numGeometries(); ++i )
4599 {
4600 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4601 if ( !curvePolygon )
4602 continue;
4603
4604 return QVariant( curvePolygon->isEmpty() ? 0 : curvePolygon->numInteriorRings() );
4605 }
4606 }
4607
4608 return QVariant();
4609}
4610
4611static QVariant fcnGeomNumRings( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4612{
4613 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4614
4615 if ( geom.isNull() )
4616 return QVariant();
4617
4619 if ( curvePolygon )
4620 return QVariant( curvePolygon->ringCount() );
4621
4622 bool foundPoly = false;
4623 int ringCount = 0;
4625 if ( collection )
4626 {
4627 //find CurvePolygons in collection
4628 for ( int i = 0; i < collection->numGeometries(); ++i )
4629 {
4630 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon *>( collection->geometryN( i ) );
4631 if ( !curvePolygon )
4632 continue;
4633
4634 foundPoly = true;
4635 ringCount += curvePolygon->ringCount();
4636 }
4637 }
4638
4639 if ( !foundPoly )
4640 return QVariant();
4641
4642 return QVariant( ringCount );
4643}
4644
4645static QVariant fcnBounds( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4646{
4647 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4648 QgsGeometry geomBounds = QgsGeometry::fromRect( geom.boundingBox() );
4649 QVariant result = !geomBounds.isNull() ? QVariant::fromValue( geomBounds ) : QVariant();
4650 return result;
4651}
4652
4653static QVariant fcnBoundsWidth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4654{
4655 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4656 return QVariant::fromValue( geom.boundingBox().width() );
4657}
4658
4659static QVariant fcnBoundsHeight( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4660{
4661 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4662 return QVariant::fromValue( geom.boundingBox().height() );
4663}
4664
4665static QVariant fcnGeometryType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4666{
4667 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4668 if ( geom.isNull() )
4669 return QVariant();
4670
4672}
4673
4674static QVariant fcnXMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4675{
4676 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4677 return QVariant::fromValue( geom.boundingBox().xMinimum() );
4678}
4679
4680static QVariant fcnXMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4681{
4682 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4683 return QVariant::fromValue( geom.boundingBox().xMaximum() );
4684}
4685
4686static QVariant fcnYMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4687{
4688 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4689 return QVariant::fromValue( geom.boundingBox().yMinimum() );
4690}
4691
4692static QVariant fcnYMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4693{
4694 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4695 return QVariant::fromValue( geom.boundingBox().yMaximum() );
4696}
4697
4698static QVariant fcnZMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4699{
4700 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4701
4702 if ( geom.isNull() || geom.isEmpty( ) )
4703 return QVariant();
4704
4705 if ( !geom.constGet()->is3D() )
4706 return QVariant();
4707
4708 double max = std::numeric_limits< double >::lowest();
4709
4710 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4711 {
4712 double z = ( *it ).z();
4713
4714 if ( max < z )
4715 max = z;
4716 }
4717
4718 if ( max == std::numeric_limits< double >::lowest() )
4719 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4720
4721 return QVariant( max );
4722}
4723
4724static QVariant fcnZMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4725{
4726 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4727
4728 if ( geom.isNull() || geom.isEmpty() )
4729 return QVariant();
4730
4731 if ( !geom.constGet()->is3D() )
4732 return QVariant();
4733
4734 double min = std::numeric_limits< double >::max();
4735
4736 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4737 {
4738 double z = ( *it ).z();
4739
4740 if ( z < min )
4741 min = z;
4742 }
4743
4744 if ( min == std::numeric_limits< double >::max() )
4745 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4746
4747 return QVariant( min );
4748}
4749
4750static QVariant fcnMMin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4751{
4752 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4753
4754 if ( geom.isNull() || geom.isEmpty() )
4755 return QVariant();
4756
4757 if ( !geom.constGet()->isMeasure() )
4758 return QVariant();
4759
4760 double min = std::numeric_limits< double >::max();
4761
4762 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4763 {
4764 double m = ( *it ).m();
4765
4766 if ( m < min )
4767 min = m;
4768 }
4769
4770 if ( min == std::numeric_limits< double >::max() )
4771 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4772
4773 return QVariant( min );
4774}
4775
4776static QVariant fcnMMax( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4777{
4778 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4779
4780 if ( geom.isNull() || geom.isEmpty() )
4781 return QVariant();
4782
4783 if ( !geom.constGet()->isMeasure() )
4784 return QVariant();
4785
4786 double max = std::numeric_limits< double >::lowest();
4787
4788 for ( auto it = geom.vertices_begin(); it != geom.vertices_end(); ++it )
4789 {
4790 double m = ( *it ).m();
4791
4792 if ( max < m )
4793 max = m;
4794 }
4795
4796 if ( max == std::numeric_limits< double >::lowest() )
4797 return QgsVariantUtils::createNullVariant( QMetaType::Type::Double );
4798
4799 return QVariant( max );
4800}
4801
4802static QVariant fcnSinuosity( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4803{
4804 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4806 if ( !curve )
4807 {
4808 parent->setEvalErrorString( QObject::tr( "Function `sinuosity` requires a line geometry." ) );
4809 return QVariant();
4810 }
4811
4812 return QVariant( curve->sinuosity() );
4813}
4814
4815static QVariant fcnStraightDistance2d( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4816{
4817 const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4818 const QgsCurve *curve = geom.constGet() ? qgsgeometry_cast< const QgsCurve * >( geom.constGet()->simplifiedTypeRef() ) : nullptr;
4819 if ( !curve )
4820 {
4821 parent->setEvalErrorString( QObject::tr( "Function `straight_distance_2d` requires a line geometry or a multi line geometry with a single part." ) );
4822 return QVariant();
4823 }
4824
4825 return QVariant( curve->straightDistance2d() );
4826}
4827
4828static QVariant fcnRoundness( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4829{
4830 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4832
4833 if ( !poly )
4834 {
4835 parent->setEvalErrorString( QObject::tr( "Function `roundness` requires a polygon geometry or a multi polygon geometry with a single part." ) );
4836 return QVariant();
4837 }
4838
4839 return QVariant( poly->roundness() );
4840}
4841
4842
4843
4844static QVariant fcnFlipCoordinates( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4845{
4846 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4847 if ( geom.isNull() )
4848 return QVariant();
4849
4850 std::unique_ptr< QgsAbstractGeometry > flipped( geom.constGet()->clone() );
4851 flipped->swapXy();
4852 return QVariant::fromValue( QgsGeometry( std::move( flipped ) ) );
4853}
4854
4855static QVariant fcnIsClosed( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4856{
4857 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4858 if ( fGeom.isNull() )
4859 return QVariant();
4860
4861 const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( fGeom.constGet() );
4862 if ( !curve && fGeom.isMultipart() )
4863 {
4865 {
4866 if ( collection->numGeometries() == 1 )
4867 {
4868 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
4869 }
4870 }
4871 }
4872
4873 if ( !curve )
4874 return QVariant();
4875
4876 return QVariant::fromValue( curve->isClosed() );
4877}
4878
4879static QVariant fcnCloseLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4880{
4881 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4882
4883 if ( geom.isNull() )
4884 return QVariant();
4885
4886 QVariant result;
4887 if ( !geom.isMultipart() )
4888 {
4890
4891 if ( !line )
4892 return QVariant();
4893
4894 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4895 closedLine->close();
4896
4897 result = QVariant::fromValue( QgsGeometry( std::move( closedLine ) ) );
4898 }
4899 else
4900 {
4902 if ( !collection )
4903 return QVariant();
4904
4905 std::unique_ptr< QgsGeometryCollection > closed( collection->createEmptyWithSameType() );
4906
4907 for ( int i = 0; i < collection->numGeometries(); ++i )
4908 {
4909 if ( const QgsLineString *line = qgsgeometry_cast<const QgsLineString * >( collection->geometryN( i ) ) )
4910 {
4911 std::unique_ptr< QgsLineString > closedLine( line->clone() );
4912 closedLine->close();
4913
4914 closed->addGeometry( closedLine.release() );
4915 }
4916 }
4917 result = QVariant::fromValue( QgsGeometry( std::move( closed ) ) );
4918 }
4919
4920 return result;
4921}
4922
4923static QVariant fcnIsEmpty( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4924{
4925 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4926 if ( fGeom.isNull() )
4927 return QVariant();
4928
4929 return QVariant::fromValue( fGeom.isEmpty() );
4930}
4931
4932static QVariant fcnIsEmptyOrNull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4933{
4934 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
4935 return QVariant::fromValue( true );
4936
4937 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4938 return QVariant::fromValue( fGeom.isNull() || fGeom.isEmpty() );
4939}
4940
4941static QVariant fcnRelate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4942{
4943 if ( values.length() < 2 || values.length() > 3 )
4944 return QVariant();
4945
4946 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4947 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4948
4949 if ( fGeom.isNull() || sGeom.isNull() )
4950 return QVariant();
4951
4952 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.constGet() ) );
4953
4954 if ( values.length() == 2 )
4955 {
4956 //two geometry arguments, return relation
4957 QString result = engine->relate( sGeom.constGet() );
4958 return QVariant::fromValue( result );
4959 }
4960 else
4961 {
4962 //three arguments, test pattern
4963 QString pattern = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
4964 bool result = engine->relatePattern( sGeom.constGet(), pattern );
4965 return QVariant::fromValue( result );
4966 }
4967}
4968
4969static QVariant fcnBbox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4970{
4971 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4972 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4973 return fGeom.intersects( sGeom.boundingBox() ) ? TVL_True : TVL_False;
4974}
4975static QVariant fcnDisjoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4976{
4977 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4978 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4979 return fGeom.disjoint( sGeom ) ? TVL_True : TVL_False;
4980}
4981static QVariant fcnIntersects( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4982{
4983 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4984 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4985 return fGeom.intersects( sGeom ) ? TVL_True : TVL_False;
4986}
4987static QVariant fcnTouches( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4988{
4989 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4990 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4991 return fGeom.touches( sGeom ) ? TVL_True : TVL_False;
4992}
4993static QVariant fcnCrosses( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
4994{
4995 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
4996 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
4997 return fGeom.crosses( sGeom ) ? TVL_True : TVL_False;
4998}
4999static QVariant fcnContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5000{
5001 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5002 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5003 return fGeom.contains( sGeom ) ? TVL_True : TVL_False;
5004}
5005static QVariant fcnOverlaps( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5006{
5007 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5008 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5009 return fGeom.overlaps( sGeom ) ? TVL_True : TVL_False;
5010}
5011static QVariant fcnWithin( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5012{
5013 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5014 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5015 return fGeom.within( sGeom ) ? TVL_True : TVL_False;
5016}
5017
5018static QVariant fcnBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5019{
5020 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5021 const double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5022 const int seg = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5023 const QString endCapString = QgsExpressionUtils::getStringValue( values.at( 3 ), parent ).trimmed();
5024 const QString joinString = QgsExpressionUtils::getStringValue( values.at( 4 ), parent ).trimmed();
5025 const double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5026
5028 if ( endCapString.compare( QLatin1String( "flat" ), Qt::CaseInsensitive ) == 0 )
5029 capStyle = Qgis::EndCapStyle::Flat;
5030 else if ( endCapString.compare( QLatin1String( "square" ), Qt::CaseInsensitive ) == 0 )
5031 capStyle = Qgis::EndCapStyle::Square;
5032
5034 if ( joinString.compare( QLatin1String( "miter" ), Qt::CaseInsensitive ) == 0 )
5035 joinStyle = Qgis::JoinStyle::Miter;
5036 else if ( joinString.compare( QLatin1String( "bevel" ), Qt::CaseInsensitive ) == 0 )
5037 joinStyle = Qgis::JoinStyle::Bevel;
5038
5039 QgsGeometry geom = fGeom.buffer( dist, seg, capStyle, joinStyle, miterLimit );
5040 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5041 return result;
5042}
5043
5044static QVariant fcnForceRHR( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5045{
5046 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5047 const QgsGeometry reoriented = fGeom.forceRHR();
5048 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5049}
5050
5051static QVariant fcnForcePolygonCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5052{
5053 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5054 const QgsGeometry reoriented = fGeom.forcePolygonClockwise();
5055 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5056}
5057
5058static QVariant fcnForcePolygonCCW( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5059{
5060 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5061 const QgsGeometry reoriented = fGeom.forcePolygonCounterClockwise();
5062 return !reoriented.isNull() ? QVariant::fromValue( reoriented ) : QVariant();
5063}
5064
5065static QVariant fcnWedgeBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5066{
5067 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5069 if ( !pt && fGeom.isMultipart() )
5070 {
5072 {
5073 if ( collection->numGeometries() == 1 )
5074 {
5075 pt = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5076 }
5077 }
5078 }
5079
5080 if ( !pt )
5081 {
5082 parent->setEvalErrorString( QObject::tr( "Function `wedge_buffer` requires a point value for the center." ) );
5083 return QVariant();
5084 }
5085
5086 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5087 double width = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5088 double outerRadius = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5089 double innerRadius = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5090
5091 QgsGeometry geom = QgsGeometry::createWedgeBuffer( *pt, azimuth, width, outerRadius, innerRadius );
5092 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5093 return result;
5094}
5095
5096static QVariant fcnTaperedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5097{
5098 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5099 if ( fGeom.type() != Qgis::GeometryType::Line )
5100 {
5101 parent->setEvalErrorString( QObject::tr( "Function `tapered_buffer` requires a line geometry." ) );
5102 return QVariant();
5103 }
5104
5105 double startWidth = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5106 double endWidth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5107 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
5108
5109 QgsGeometry geom = fGeom.taperedBuffer( startWidth, endWidth, segments );
5110 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5111 return result;
5112}
5113
5114static QVariant fcnBufferByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5115{
5116 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5117 if ( fGeom.type() != Qgis::GeometryType::Line )
5118 {
5119 parent->setEvalErrorString( QObject::tr( "Function `buffer_by_m` requires a line geometry." ) );
5120 return QVariant();
5121 }
5122
5123 int segments = static_cast< int >( QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) );
5124
5125 QgsGeometry geom = fGeom.variableWidthBufferByM( segments );
5126 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5127 return result;
5128}
5129
5130static QVariant fcnOffsetCurve( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5131{
5132 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5133 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5134 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5135 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5136 if ( joinInt < 1 || joinInt > 3 )
5137 return QVariant();
5138 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5139
5140 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5141
5142 QgsGeometry geom = fGeom.offsetCurve( dist, segments, join, miterLimit );
5143 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5144 return result;
5145}
5146
5147static QVariant fcnSingleSidedBuffer( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5148{
5149 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5150 double dist = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5151 int segments = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
5152
5153 const int joinInt = QgsExpressionUtils::getIntValue( values.at( 3 ), parent );
5154 if ( joinInt < 1 || joinInt > 3 )
5155 return QVariant();
5156 const Qgis::JoinStyle join = static_cast< Qgis::JoinStyle >( joinInt );
5157
5158 double miterLimit = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5159
5160 QgsGeometry geom = fGeom.singleSidedBuffer( dist, segments, Qgis::BufferSide::Left, join, miterLimit );
5161 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5162 return result;
5163}
5164
5165static QVariant fcnExtend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5166{
5167 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5168 double distStart = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5169 double distEnd = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5170
5171 QgsGeometry geom = fGeom.extendLine( distStart, distEnd );
5172 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5173 return result;
5174}
5175
5176static QVariant fcnTranslate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5177{
5178 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5179 double dx = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5180 double dy = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5181 fGeom.translate( dx, dy );
5182 return QVariant::fromValue( fGeom );
5183}
5184
5185static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5186{
5187 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5188 const double rotation = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5189 const QgsGeometry center = values.at( 2 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 2 ), parent )
5190 : QgsGeometry();
5191 const bool perPart = values.value( 3 ).toBool();
5192
5193 if ( center.isNull() && perPart && fGeom.isMultipart() )
5194 {
5195 // no explicit center, rotating per part
5196 // (note that we only do this branch for multipart geometries -- for singlepart geometries
5197 // the result is equivalent to setting perPart as false anyway)
5198 std::unique_ptr< QgsGeometryCollection > collection( qgsgeometry_cast< QgsGeometryCollection * >( fGeom.constGet()->clone() ) );
5199 for ( auto it = collection->parts_begin(); it != collection->parts_end(); ++it )
5200 {
5201 const QgsPointXY partCenter = ( *it )->boundingBox().center();
5202 QTransform t = QTransform::fromTranslate( partCenter.x(), partCenter.y() );
5203 t.rotate( -rotation );
5204 t.translate( -partCenter.x(), -partCenter.y() );
5205 ( *it )->transform( t );
5206 }
5207 return QVariant::fromValue( QgsGeometry( std::move( collection ) ) );
5208 }
5209 else
5210 {
5211 QgsPointXY pt;
5212 if ( center.isEmpty() )
5213 {
5214 // if center wasn't specified, use bounding box centroid
5215 pt = fGeom.boundingBox().center();
5216 }
5218 {
5219 parent->setEvalErrorString( QObject::tr( "Function 'rotate' requires a point value for the center" ) );
5220 return QVariant();
5221 }
5222 else
5223 {
5225 }
5226
5227 fGeom.rotate( rotation, pt );
5228 return QVariant::fromValue( fGeom );
5229 }
5230}
5231
5232static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5233{
5234 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5235 const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5236 const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5237 const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
5238 : QgsGeometry();
5239
5240 QgsPointXY pt;
5241 if ( center.isNull() )
5242 {
5243 // if center wasn't specified, use bounding box centroid
5244 pt = fGeom.boundingBox().center();
5245 }
5247 {
5248 parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
5249 return QVariant();
5250 }
5251 else
5252 {
5253 pt = center.asPoint();
5254 }
5255
5256 QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
5257 t.scale( xScale, yScale );
5258 t.translate( -pt.x(), -pt.y() );
5259 fGeom.transform( t );
5260 return QVariant::fromValue( fGeom );
5261}
5262
5263static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5264{
5265 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5266 if ( fGeom.isNull() )
5267 {
5268 return QVariant();
5269 }
5270
5271 const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5272 const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5273
5274 const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5275
5276 const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
5277 const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );
5278
5279 const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
5280 const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
5281 const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
5282 const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );
5283
5284 if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
5285 {
5286 fGeom.get()->addZValue( 0 );
5287 }
5288 if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
5289 {
5290 fGeom.get()->addMValue( 0 );
5291 }
5292
5293 QTransform transform;
5294 transform.translate( deltaX, deltaY );
5295 transform.rotate( rotationZ );
5296 transform.scale( scaleX, scaleY );
5297 fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );
5298
5299 return QVariant::fromValue( fGeom );
5300}
5301
5302
5303static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5304{
5305 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5306 QgsGeometry geom = fGeom.centroid();
5307 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5308 return result;
5309}
5310static QVariant fcnPointOnSurface( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5311{
5312 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5313 QgsGeometry geom = fGeom.pointOnSurface();
5314 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5315 return result;
5316}
5317
5318static QVariant fcnPoleOfInaccessibility( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5319{
5320 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5321 double tolerance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5322 QgsGeometry geom = fGeom.poleOfInaccessibility( tolerance );
5323 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5324 return result;
5325}
5326
5327static QVariant fcnConvexHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5328{
5329 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5330 QgsGeometry geom = fGeom.convexHull();
5331 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5332 return result;
5333}
5334
5335#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
5336static QVariant fcnConcaveHull( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5337{
5338 try
5339 {
5340 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5341 const double targetPercent = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5342 const bool allowHoles = values.value( 2 ).toBool();
5343 QgsGeometry geom = fGeom.concaveHull( targetPercent, allowHoles );
5344 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5345 return result;
5346 }
5347 catch ( QgsCsException &cse )
5348 {
5349 QgsMessageLog::logMessage( QObject::tr( "Error caught in concave_hull() function: %1" ).arg( cse.what() ) );
5350 return QVariant();
5351 }
5352}
5353#endif
5354
5355static QVariant fcnMinimalCircle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5356{
5357 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5358 int segments = 36;
5359 if ( values.length() == 2 )
5360 segments = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5361 if ( segments < 0 )
5362 {
5363 parent->setEvalErrorString( QObject::tr( "Parameter can not be negative." ) );
5364 return QVariant();
5365 }
5366
5367 QgsGeometry geom = fGeom.minimalEnclosingCircle( static_cast<unsigned int>( segments ) );
5368 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5369 return result;
5370}
5371
5372static QVariant fcnOrientedBBox( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5373{
5374 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5376 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5377 return result;
5378}
5379
5380static QVariant fcnMainAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5381{
5382 const QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5383
5384 // we use the angle of the oriented minimum bounding box to calculate the polygon main angle.
5385 // While ArcGIS uses a different approach ("the angle of longest collection of segments that have similar orientation"), this
5386 // yields similar results to OMBB approach under the same constraints ("this tool is meant for primarily orthogonal polygons rather than organically shaped ones.")
5387
5388 double area, angle, width, height;
5389 const QgsGeometry geom = fGeom.orientedMinimumBoundingBox( area, angle, width, height );
5390
5391 if ( geom.isNull() )
5392 {
5393 parent->setEvalErrorString( QObject::tr( "Error calculating polygon main angle: %1" ).arg( geom.lastError() ) );
5394 return QVariant();
5395 }
5396 return angle;
5397}
5398
5399static QVariant fcnDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5400{
5401 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5402 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5403 QgsGeometry geom = fGeom.difference( sGeom );
5404 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5405 return result;
5406}
5407
5408static QVariant fcnReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5409{
5410 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
5411 return QVariant();
5412
5413 // two variants, one for geometry, one for string
5414
5415 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent, true );
5416 if ( !fGeom.isNull() )
5417 {
5418 QVariant result;
5419 if ( !fGeom.isMultipart() )
5420 {
5421 const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( fGeom.constGet() );
5422 if ( !curve )
5423 return QVariant();
5424
5425 QgsCurve *reversed = curve->reversed();
5426 result = reversed ? QVariant::fromValue( QgsGeometry( reversed ) ) : QVariant();
5427 }
5428 else
5429 {
5431 std::unique_ptr< QgsGeometryCollection > reversed( collection->createEmptyWithSameType() );
5432 for ( int i = 0; i < collection->numGeometries(); ++i )
5433 {
5434 if ( const QgsCurve *curve = qgsgeometry_cast<const QgsCurve * >( collection->geometryN( i ) ) )
5435 {
5436 reversed->addGeometry( curve->reversed() );
5437 }
5438 else
5439 {
5440 reversed->addGeometry( collection->geometryN( i )->clone() );
5441 }
5442 }
5443 result = reversed ? QVariant::fromValue( QgsGeometry( std::move( reversed ) ) ) : QVariant();
5444 }
5445 return result;
5446 }
5447
5448 //fall back to string variant
5449 QString string = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
5450 std::reverse( string.begin(), string.end() );
5451 return string;
5452}
5453
5454static QVariant fcnExteriorRing( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5455{
5456 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5457 if ( fGeom.isNull() )
5458 return QVariant();
5459
5461 if ( !curvePolygon && fGeom.isMultipart() )
5462 {
5464 {
5465 if ( collection->numGeometries() == 1 )
5466 {
5467 curvePolygon = qgsgeometry_cast< const QgsCurvePolygon * >( collection->geometryN( 0 ) );
5468 }
5469 }
5470 }
5471
5472 if ( !curvePolygon || !curvePolygon->exteriorRing() )
5473 return QVariant();
5474
5475 QgsCurve *exterior = static_cast< QgsCurve * >( curvePolygon->exteriorRing()->clone() );
5476 QVariant result = exterior ? QVariant::fromValue( QgsGeometry( exterior ) ) : QVariant();
5477 return result;
5478}
5479
5480static QVariant fcnDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5481{
5482 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5483 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5484 return QVariant( fGeom.distance( sGeom ) );
5485}
5486
5487static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5488{
5489 QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5490 QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5491
5492 double res = -1;
5493 if ( values.length() == 3 && values.at( 2 ).isValid() )
5494 {
5495 double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5496 densify = std::clamp( densify, 0.0, 1.0 );
5497 res = g1.hausdorffDistanceDensify( g2, densify );
5498 }
5499 else
5500 {
5501 res = g1.hausdorffDistance( g2 );
5502 }
5503
5504 return res > -1 ? QVariant( res ) : QVariant();
5505}
5506
5507static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5508{
5509 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5510 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5511 QgsGeometry geom = fGeom.intersection( sGeom );
5512 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5513 return result;
5514}
5515static QVariant fcnSymDifference( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5516{
5517 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5518 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5519 QgsGeometry geom = fGeom.symDifference( sGeom );
5520 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5521 return result;
5522}
5523static QVariant fcnCombine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5524{
5525 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5526 QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5527 QgsGeometry geom = fGeom.combine( sGeom );
5528 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5529 return result;
5530}
5531
5532static QVariant fcnGeomToWKT( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5533{
5534 if ( values.length() < 1 || values.length() > 2 )
5535 return QVariant();
5536
5537 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5538 int prec = 8;
5539 if ( values.length() == 2 )
5540 prec = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5541 QString wkt = fGeom.asWkt( prec );
5542 return QVariant( wkt );
5543}
5544
5545static QVariant fcnGeomToWKB( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5546{
5547 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5548 return fGeom.isNull() ? QVariant() : QVariant( fGeom.asWkb() );
5549}
5550
5551static QVariant fcnAzimuth( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5552{
5553 if ( values.length() != 2 )
5554 {
5555 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires exactly two parameters. %n given.", nullptr, values.length() ) );
5556 return QVariant();
5557 }
5558
5559 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5560 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5561
5562 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5563 if ( !pt1 && fGeom1.isMultipart() )
5564 {
5566 {
5567 if ( collection->numGeometries() == 1 )
5568 {
5569 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5570 }
5571 }
5572 }
5573
5574 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5575 if ( !pt2 && fGeom2.isMultipart() )
5576 {
5578 {
5579 if ( collection->numGeometries() == 1 )
5580 {
5581 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5582 }
5583 }
5584 }
5585
5586 if ( !pt1 || !pt2 )
5587 {
5588 parent->setEvalErrorString( QObject::tr( "Function `azimuth` requires two points as arguments." ) );
5589 return QVariant();
5590 }
5591
5592 // Code from PostGIS
5593 if ( qgsDoubleNear( pt1->x(), pt2->x() ) )
5594 {
5595 if ( pt1->y() < pt2->y() )
5596 return 0.0;
5597 else if ( pt1->y() > pt2->y() )
5598 return M_PI;
5599 else
5600 return 0;
5601 }
5602
5603 if ( qgsDoubleNear( pt1->y(), pt2->y() ) )
5604 {
5605 if ( pt1->x() < pt2->x() )
5606 return M_PI_2;
5607 else if ( pt1->x() > pt2->x() )
5608 return M_PI + ( M_PI_2 );
5609 else
5610 return 0;
5611 }
5612
5613 if ( pt1->x() < pt2->x() )
5614 {
5615 if ( pt1->y() < pt2->y() )
5616 {
5617 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) );
5618 }
5619 else /* ( pt1->y() > pt2->y() ) - equality case handled above */
5620 {
5621 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5622 + ( M_PI_2 );
5623 }
5624 }
5625
5626 else /* ( pt1->x() > pt2->x() ) - equality case handled above */
5627 {
5628 if ( pt1->y() > pt2->y() )
5629 {
5630 return std::atan( std::fabs( pt1->x() - pt2->x() ) / std::fabs( pt1->y() - pt2->y() ) )
5631 + M_PI;
5632 }
5633 else /* ( pt1->y() < pt2->y() ) - equality case handled above */
5634 {
5635 return std::atan( std::fabs( pt1->y() - pt2->y() ) / std::fabs( pt1->x() - pt2->x() ) )
5636 + ( M_PI + ( M_PI_2 ) );
5637 }
5638 }
5639}
5640
5641static QVariant fcnBearing( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
5642{
5643 const QgsGeometry geom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5644 const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5645 QgsCoordinateReferenceSystem sourceCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
5646 QString ellipsoid = QgsExpressionUtils::getStringValue( values.at( 3 ), parent );
5647
5648 if ( geom1.isNull() || geom2.isNull() || geom1.type() != Qgis::GeometryType::Point || geom2.type() != Qgis::GeometryType::Point )
5649 {
5650 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires two valid point geometries." ) );
5651 return QVariant();
5652 }
5653
5654 const QgsPointXY point1 = geom1.asPoint();
5655 const QgsPointXY point2 = geom2.asPoint();
5656 if ( point1.isEmpty() || point2.isEmpty() )
5657 {
5658 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires point geometries or multi point geometries with a single part." ) );
5659 return QVariant();
5660 }
5661
5663 if ( context )
5664 {
5665 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
5666
5667 if ( !sourceCrs.isValid() )
5668 {
5669 sourceCrs = context->variable( QStringLiteral( "_layer_crs" ) ).value<QgsCoordinateReferenceSystem>();
5670 }
5671
5672 if ( ellipsoid.isEmpty() )
5673 {
5674 ellipsoid = context->variable( QStringLiteral( "project_ellipsoid" ) ).toString();
5675 }
5676 }
5677
5678 if ( !sourceCrs.isValid() )
5679 {
5680 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid source CRS." ) );
5681 return QVariant();
5682 }
5683
5684 QgsDistanceArea da;
5685 da.setSourceCrs( sourceCrs, tContext );
5686 if ( !da.setEllipsoid( ellipsoid ) )
5687 {
5688 parent->setEvalErrorString( QObject::tr( "Function `bearing` requires a valid ellipsoid acronym or ellipsoid authority ID." ) );
5689 return QVariant();
5690 }
5691
5692 try
5693 {
5694 const double bearing = da.bearing( point1, point2 );
5695 if ( std::isfinite( bearing ) )
5696 {
5697 return std::fmod( bearing + 2 * M_PI, 2 * M_PI );
5698 }
5699 }
5700 catch ( QgsCsException &cse )
5701 {
5702 QgsMessageLog::logMessage( QObject::tr( "Error caught in bearing() function: %1" ).arg( cse.what() ) );
5703 return QVariant();
5704 }
5705 return QVariant();
5706}
5707
5708static QVariant fcnProject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5709{
5710 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5711
5713 {
5714 parent->setEvalErrorString( QStringLiteral( "'project' requires a point geometry" ) );
5715 return QVariant();
5716 }
5717
5718 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5719 double azimuth = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5720 double inclination = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );
5721
5722 const QgsPoint *p = static_cast<const QgsPoint *>( geom.constGet()->simplifiedTypeRef( ) );
5723 QgsPoint newPoint = p->project( distance, 180.0 * azimuth / M_PI, 180.0 * inclination / M_PI );
5724
5725 return QVariant::fromValue( QgsGeometry( new QgsPoint( newPoint ) ) );
5726}
5727
5728static QVariant fcnInclination( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5729{
5730 QgsGeometry fGeom1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5731 QgsGeometry fGeom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5732
5733 const QgsPoint *pt1 = qgsgeometry_cast<const QgsPoint *>( fGeom1.constGet() );
5734 if ( !pt1 && fGeom1.isMultipart() )
5735 {
5737 {
5738 if ( collection->numGeometries() == 1 )
5739 {
5740 pt1 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5741 }
5742 }
5743 }
5744 const QgsPoint *pt2 = qgsgeometry_cast<const QgsPoint *>( fGeom2.constGet() );
5745 if ( !pt2 && fGeom2.isMultipart() )
5746 {
5748 {
5749 if ( collection->numGeometries() == 1 )
5750 {
5751 pt2 = qgsgeometry_cast< const QgsPoint * >( collection->geometryN( 0 ) );
5752 }
5753 }
5754 }
5755
5756 if ( ( fGeom1.type() != Qgis::GeometryType::Point ) || ( fGeom2.type() != Qgis::GeometryType::Point ) ||
5757 !pt1 || !pt2 )
5758 {
5759 parent->setEvalErrorString( QStringLiteral( "Function 'inclination' requires two points as arguments." ) );
5760 return QVariant();
5761 }
5762
5763 return pt1->inclination( *pt2 );
5764
5765}
5766
5767static QVariant fcnExtrude( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5768{
5769 if ( values.length() != 3 )
5770 return QVariant();
5771
5772 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5773 double x = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5774 double y = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5775
5776 QgsGeometry geom = fGeom.extrude( x, y );
5777
5778 QVariant result = geom.constGet() ? QVariant::fromValue( geom ) : QVariant();
5779 return result;
5780}
5781
5782static QVariant fcnOrderParts( const QVariantList &values, const QgsExpressionContext *ctx, QgsExpression *parent, const QgsExpressionNodeFunction * )
5783{
5784 if ( values.length() < 2 )
5785 return QVariant();
5786
5787 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5788
5789 if ( !fGeom.isMultipart() )
5790 return values.at( 0 );
5791
5792 QString expString = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
5793 QVariant cachedExpression;
5794 if ( ctx )
5795 cachedExpression = ctx->cachedValue( expString );
5796 QgsExpression expression;
5797
5798 if ( cachedExpression.isValid() )
5799 {
5800 expression = cachedExpression.value<QgsExpression>();
5801 }
5802 else
5803 expression = QgsExpression( expString );
5804
5805 bool asc = values.value( 2 ).toBool();
5806
5807 QgsExpressionContext *unconstedContext = nullptr;
5808 QgsFeature f;
5809 if ( ctx )
5810 {
5811 // ExpressionSorter wants a modifiable expression context, but it will return it in the same shape after
5812 // so no reason to worry
5813 unconstedContext = const_cast<QgsExpressionContext *>( ctx );
5814 f = ctx->feature();
5815 }
5816 else
5817 {
5818 // If there's no context provided, create a fake one
5819 unconstedContext = new QgsExpressionContext();
5820 }
5821
5823 Q_ASSERT( collection ); // Should have failed the multipart check above
5824
5826 orderBy.append( QgsFeatureRequest::OrderByClause( expression, asc ) );
5827 QgsExpressionSorter sorter( orderBy );
5828
5829 QList<QgsFeature> partFeatures;
5830 partFeatures.reserve( collection->partCount() );
5831 for ( int i = 0; i < collection->partCount(); ++i )
5832 {
5833 f.setGeometry( QgsGeometry( collection->geometryN( i )->clone() ) );
5834 partFeatures << f;
5835 }
5836
5837 sorter.sortFeatures( partFeatures, unconstedContext );
5838
5840
5841 Q_ASSERT( orderedGeom );
5842
5843 while ( orderedGeom->partCount() )
5844 orderedGeom->removeGeometry( 0 );
5845
5846 for ( const QgsFeature &feature : std::as_const( partFeatures ) )
5847 {
5848 orderedGeom->addGeometry( feature.geometry().constGet()->clone() );
5849 }
5850
5851 QVariant result = QVariant::fromValue( QgsGeometry( orderedGeom ) );
5852
5853 if ( !ctx )
5854 delete unconstedContext;
5855
5856 return result;
5857}
5858
5859static QVariant fcnClosestPoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5860{
5861 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5862 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5863
5864 QgsGeometry geom = fromGeom.nearestPoint( toGeom );
5865
5866 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5867 return result;
5868}
5869
5870static QVariant fcnShortestLine( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5871{
5872 QgsGeometry fromGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5873 QgsGeometry toGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5874
5875 QgsGeometry geom = fromGeom.shortestLine( toGeom );
5876
5877 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5878 return result;
5879}
5880
5881static QVariant fcnLineInterpolatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5882{
5883 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5884 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5885
5886 QgsGeometry geom = lineGeom.interpolate( distance );
5887
5888 QVariant result = !geom.isNull() ? QVariant::fromValue( geom ) : QVariant();
5889 return result;
5890}
5891
5892static QVariant fcnLineInterpolatePointByM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5893{
5894 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5895 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5896 const bool use3DDistance = values.at( 2 ).toBool();
5897
5898 double x, y, z, distance;
5899
5901 if ( !line )
5902 {
5903 return QVariant();
5904 }
5905
5906 if ( line->lineLocatePointByM( m, x, y, z, distance, use3DDistance ) )
5907 {
5908 QgsPoint point( x, y );
5909 if ( use3DDistance && QgsWkbTypes::hasZ( lineGeom.wkbType() ) )
5910 {
5911 point.addZValue( z );
5912 }
5913 return QVariant::fromValue( QgsGeometry( point.clone() ) );
5914 }
5915
5916 return QVariant();
5917}
5918
5919static QVariant fcnLineSubset( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5920{
5921 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5922 if ( lineGeom.type() != Qgis::GeometryType::Line )
5923 {
5924 parent->setEvalErrorString( QObject::tr( "line_substring requires a curve geometry input" ) );
5925 return QVariant();
5926 }
5927
5928 const QgsCurve *curve = nullptr;
5929 if ( !lineGeom.isMultipart() )
5930 curve = qgsgeometry_cast< const QgsCurve * >( lineGeom.constGet() );
5931 else
5932 {
5934 {
5935 if ( collection->numGeometries() > 0 )
5936 {
5937 curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
5938 }
5939 }
5940 }
5941 if ( !curve )
5942 return QVariant();
5943
5944 double startDistance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5945 double endDistance = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
5946
5947 std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
5948 QgsGeometry result( std::move( substring ) );
5949 return !result.isNull() ? QVariant::fromValue( result ) : QVariant();
5950}
5951
5952static QVariant fcnLineInterpolateAngle( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5953{
5954 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5955 double distance = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
5956
5957 return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI;
5958}
5959
5960static QVariant fcnAngleAtVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5961{
5962 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5963 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5964 if ( vertex < 0 )
5965 {
5966 //negative idx
5967 int count = geom.constGet()->nCoordinates();
5968 vertex = count + vertex;
5969 }
5970
5971 return geom.angleAtVertex( vertex ) * 180.0 / M_PI;
5972}
5973
5974static QVariant fcnDistanceToVertex( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5975{
5976 QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5977 int vertex = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
5978 if ( vertex < 0 )
5979 {
5980 //negative idx
5981 int count = geom.constGet()->nCoordinates();
5982 vertex = count + vertex;
5983 }
5984
5985 return geom.distanceToVertex( vertex );
5986}
5987
5988static QVariant fcnLineLocatePoint( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5989{
5990 QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
5991 QgsGeometry pointGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
5992
5993 double distance = lineGeom.lineLocatePoint( pointGeom );
5994
5995 return distance >= 0 ? distance : QVariant();
5996}
5997
5998static QVariant fcnLineLocateM( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
5999{
6000 const QgsGeometry lineGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6001 const double m = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6002 const bool use3DDistance = values.at( 2 ).toBool();
6003
6004 double x, y, z, distance;
6005
6007 if ( !line )
6008 {
6009 return QVariant();
6010 }
6011
6012 const bool found = line->lineLocatePointByM( m, x, y, z, distance, use3DDistance );
6013 return found ? distance : QVariant();
6014}
6015
6016static QVariant fcnRound( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6017{
6018 if ( values.length() == 2 && values.at( 1 ).toInt() != 0 )
6019 {
6020 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6021 return qgsRound( number, QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6022 }
6023
6024 if ( values.length() >= 1 )
6025 {
6026 double number = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6027 return QVariant( qlonglong( std::round( number ) ) );
6028 }
6029
6030 return QVariant();
6031}
6032
6033static QVariant fcnPi( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6034{
6035 Q_UNUSED( values )
6036 Q_UNUSED( parent )
6037 return M_PI;
6038}
6039
6040static QVariant fcnFormatNumber( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6041{
6042 const double value = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
6043 const int places = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6044 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6045 if ( places < 0 )
6046 {
6047 parent->setEvalErrorString( QObject::tr( "Number of places must be positive" ) );
6048 return QVariant();
6049 }
6050
6051 const bool omitGroupSeparator = values.value( 3 ).toBool();
6052 const bool trimTrailingZeros = values.value( 4 ).toBool();
6053
6054 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6055 if ( !omitGroupSeparator )
6056 locale.setNumberOptions( locale.numberOptions() & ~QLocale::NumberOption::OmitGroupSeparator );
6057 else
6058 locale.setNumberOptions( locale.numberOptions() | QLocale::NumberOption::OmitGroupSeparator );
6059
6060 QString res = locale.toString( value, 'f', places );
6061
6062 if ( trimTrailingZeros )
6063 {
6064#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
6065 const QChar decimal = locale.decimalPoint();
6066 const QChar zeroDigit = locale.zeroDigit();
6067#else
6068 const QChar decimal = locale.decimalPoint().at( 0 );
6069 const QChar zeroDigit = locale.zeroDigit().at( 0 );
6070#endif
6071
6072 if ( res.contains( decimal ) )
6073 {
6074 int trimPoint = res.length() - 1;
6075
6076 while ( res.at( trimPoint ) == zeroDigit )
6077 trimPoint--;
6078
6079 if ( res.at( trimPoint ) == decimal )
6080 trimPoint--;
6081
6082 res.truncate( trimPoint + 1 );
6083 }
6084 }
6085
6086 return res;
6087}
6088
6089static QVariant fcnFormatDate( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6090{
6091 QDateTime datetime = QgsExpressionUtils::getDateTimeValue( values.at( 0 ), parent );
6092 const QString format = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6093 const QString language = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
6094
6095 // Convert to UTC if the format string includes a Z, as QLocale::toString() doesn't do it
6096 if ( format.indexOf( "Z" ) > 0 )
6097 datetime = datetime.toUTC();
6098
6099 QLocale locale = !language.isEmpty() ? QLocale( language ) : QLocale();
6100 return locale.toString( datetime, format );
6101}
6102
6103static QVariant fcnColorGrayscaleAverage( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6104{
6105 const QVariant variant = values.at( 0 );
6106 bool isQColor;
6107 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6108 if ( !color.isValid() )
6109 return QVariant();
6110
6111 const float alpha = color.alphaF(); // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
6112 if ( color.spec() == QColor::Spec::Cmyk )
6113 {
6114 const float avg = ( color.cyanF() + color.magentaF() + color.yellowF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
6115 color = QColor::fromCmykF( avg, avg, avg, color.blackF(), alpha );
6116 }
6117 else
6118 {
6119 const float avg = ( color.redF() + color.greenF() + color.blueF() ) / 3; // NOLINT(bugprone-narrowing-conversions): TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
6120 color.setRgbF( avg, avg, avg, alpha );
6121 }
6122
6123 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6124}
6125
6126static QVariant fcnColorMixRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6127{
6128 QColor color1 = QgsSymbolLayerUtils::decodeColor( values.at( 0 ).toString() );
6129 QColor color2 = QgsSymbolLayerUtils::decodeColor( values.at( 1 ).toString() );
6130 double ratio = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
6131 if ( ratio > 1 )
6132 {
6133 ratio = 1;
6134 }
6135 else if ( ratio < 0 )
6136 {
6137 ratio = 0;
6138 }
6139
6140 int red = static_cast<int>( color1.red() * ( 1 - ratio ) + color2.red() * ratio );
6141 int green = static_cast<int>( color1.green() * ( 1 - ratio ) + color2.green() * ratio );
6142 int blue = static_cast<int>( color1.blue() * ( 1 - ratio ) + color2.blue() * ratio );
6143 int alpha = static_cast<int>( color1.alpha() * ( 1 - ratio ) + color2.alpha() * ratio );
6144
6145 QColor newColor( red, green, blue, alpha );
6146
6147 return QgsSymbolLayerUtils::encodeColor( newColor );
6148}
6149
6150static QVariant fcnColorMix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6151{
6152 const QVariant variant1 = values.at( 0 );
6153 const QVariant variant2 = values.at( 1 );
6154
6155 if ( variant1.userType() != variant2.userType() )
6156 {
6157 parent->setEvalErrorString( QObject::tr( "Both color arguments must have the same type (string or color object)" ) );
6158 return QVariant();
6159 }
6160
6161 bool isQColor;
6162 const QColor color1 = QgsExpressionUtils::getColorValue( variant1, parent, isQColor );
6163 if ( !color1.isValid() )
6164 return QVariant();
6165
6166 const QColor color2 = QgsExpressionUtils::getColorValue( variant2, parent, isQColor );
6167 if ( !color2.isValid() )
6168 return QVariant();
6169
6170 if ( ( color1.spec() == QColor::Cmyk ) != ( color2.spec() == QColor::Cmyk ) )
6171 {
6172 parent->setEvalErrorString( QObject::tr( "Both color arguments must have compatible color type (CMYK or RGB/HSV/HSL)" ) );
6173 return QVariant();
6174 }
6175
6176 const float ratio = static_cast<float>( std::clamp( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ), 0., 1. ) );
6177
6178 // TODO QGIS 4 remove the nolint instructions, QColor was qreal (double) and is now float
6179 // NOLINTBEGIN(bugprone-narrowing-conversions)
6180
6181 QColor newColor;
6182 const float alpha = color1.alphaF() * ( 1 - ratio ) + color2.alphaF() * ratio;
6183 if ( color1.spec() == QColor::Spec::Cmyk )
6184 {
6185 float cyan = color1.cyanF() * ( 1 - ratio ) + color2.cyanF() * ratio;
6186 float magenta = color1.magentaF() * ( 1 - ratio ) + color2.magentaF() * ratio;
6187 float yellow = color1.yellowF() * ( 1 - ratio ) + color2.yellowF() * ratio;
6188 float black = color1.blackF() * ( 1 - ratio ) + color2.blackF() * ratio;
6189 newColor = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6190 }
6191 else
6192 {
6193 float red = color1.redF() * ( 1 - ratio ) + color2.redF() * ratio;
6194 float green = color1.greenF() * ( 1 - ratio ) + color2.greenF() * ratio;
6195 float blue = color1.blueF() * ( 1 - ratio ) + color2.blueF() * ratio;
6196 newColor = QColor::fromRgbF( red, green, blue, alpha );
6197 }
6198
6199 // NOLINTEND(bugprone-narrowing-conversions)
6200
6201 return isQColor ? QVariant( newColor ) : QVariant( QgsSymbolLayerUtils::encodeColor( newColor ) );
6202}
6203
6204static QVariant fcnColorRgb( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6205{
6206 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6207 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6208 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6209 QColor color = QColor( red, green, blue );
6210 if ( ! color.isValid() )
6211 {
6212 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( red ).arg( green ).arg( blue ) );
6213 color = QColor( 0, 0, 0 );
6214 }
6215
6216 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6217}
6218
6219static QVariant fcnColorRgbF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6220{
6221 const float red = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6222 const float green = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6223 const float blue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6224 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6225 QColor color = QColor::fromRgbF( red, green, blue, alpha );
6226 if ( ! color.isValid() )
6227 {
6228 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6229 return QVariant();
6230 }
6231
6232 return color;
6233}
6234
6235static QVariant fcnTry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6236{
6237 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6238 QVariant value = node->eval( parent, context );
6239 if ( parent->hasEvalError() )
6240 {
6241 parent->setEvalErrorString( QString() );
6242 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6244 value = node->eval( parent, context );
6246 }
6247 return value;
6248}
6249
6250static QVariant fcnIf( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6251{
6252 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
6254 QVariant value = node->eval( parent, context );
6256 if ( value.toBool() )
6257 {
6258 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
6260 value = node->eval( parent, context );
6262 }
6263 else
6264 {
6265 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
6267 value = node->eval( parent, context );
6269 }
6270 return value;
6271}
6272
6273static QVariant fncColorRgba( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6274{
6275 int red = QgsExpressionUtils::getNativeIntValue( values.at( 0 ), parent );
6276 int green = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
6277 int blue = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6278 int alpha = QgsExpressionUtils::getNativeIntValue( values.at( 3 ), parent );
6279 QColor color = QColor( red, green, blue, alpha );
6280 if ( ! color.isValid() )
6281 {
6282 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( red ).arg( green ).arg( blue ).arg( alpha ) );
6283 color = QColor( 0, 0, 0 );
6284 }
6285 return QgsSymbolLayerUtils::encodeColor( color );
6286}
6287
6288QVariant fcnRampColorObject( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6289{
6290 QgsGradientColorRamp expRamp;
6291 const QgsColorRamp *ramp = nullptr;
6292 if ( values.at( 0 ).userType() == qMetaTypeId< QgsGradientColorRamp>() )
6293 {
6294 expRamp = QgsExpressionUtils::getRamp( values.at( 0 ), parent );
6295 ramp = &expRamp;
6296 }
6297 else
6298 {
6299 QString rampName = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
6300 ramp = QgsStyle::defaultStyle()->colorRampRef( rampName );
6301 if ( ! ramp )
6303 parent->setEvalErrorString( QObject::tr( "\"%1\" is not a valid color ramp" ).arg( rampName ) );
6304 return QVariant();
6305 }
6306 }
6307
6308 double value = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
6309 QColor color = ramp->color( value );
6310 return color;
6311}
6312
6313QVariant fcnRampColor( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6314{
6315 QColor color = fcnRampColorObject( values, context, parent, node ).value<QColor>();
6316 return color.isValid() ? QgsSymbolLayerUtils::encodeColor( color ) : QVariant();
6317}
6318
6319static QVariant fcnColorHsl( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6320{
6321 // Hue ranges from 0 - 360
6322 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6323 // Saturation ranges from 0 - 100
6324 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6325 // Lightness ranges from 0 - 100
6326 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6327
6328 QColor color = QColor::fromHslF( hue, saturation, lightness );
6329
6330 if ( ! color.isValid() )
6331 {
6332 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( lightness ) );
6333 color = QColor( 0, 0, 0 );
6334 }
6335
6336 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6337}
6338
6339static QVariant fncColorHsla( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6340{
6341 // Hue ranges from 0 - 360
6342 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6343 // Saturation ranges from 0 - 100
6344 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6345 // Lightness ranges from 0 - 100
6346 double lightness = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6347 // Alpha ranges from 0 - 255
6348 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6349
6350 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6351 if ( ! color.isValid() )
6352 {
6353 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6354 color = QColor( 0, 0, 0 );
6355 }
6356 return QgsSymbolLayerUtils::encodeColor( color );
6357}
6358
6359static QVariant fcnColorHslF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6360{
6361 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6362 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6363 float lightness = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6364 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6365
6366 QColor color = QColor::fromHslF( hue, saturation, lightness, alpha );
6367 if ( ! color.isValid() )
6368 {
6369 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( lightness ).arg( alpha ) );
6370 return QVariant();
6371 }
6372
6373 return color;
6374}
6375
6376static QVariant fcnColorHsv( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6377{
6378 // Hue ranges from 0 - 360
6379 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6380 // Saturation ranges from 0 - 100
6381 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6382 // Value ranges from 0 - 100
6383 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6384
6385 QColor color = QColor::fromHsvF( hue, saturation, value );
6386
6387 if ( ! color.isValid() )
6388 {
6389 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3' to color" ).arg( hue ).arg( saturation ).arg( value ) );
6390 color = QColor( 0, 0, 0 );
6391 }
6392
6393 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6394}
6395
6396static QVariant fncColorHsva( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6397{
6398 // Hue ranges from 0 - 360
6399 double hue = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 360.0;
6400 // Saturation ranges from 0 - 100
6401 double saturation = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6402 // Value ranges from 0 - 100
6403 double value = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6404 // Alpha ranges from 0 - 255
6405 double alpha = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 255.0;
6406
6407 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6408 if ( ! color.isValid() )
6409 {
6410 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6411 color = QColor( 0, 0, 0 );
6412 }
6413 return QgsSymbolLayerUtils::encodeColor( color );
6414}
6415
6416static QVariant fcnColorHsvF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6417{
6418 float hue = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6419 float saturation = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6420 float value = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6421 float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6422 QColor color = QColor::fromHsvF( hue, saturation, value, alpha );
6423
6424 if ( ! color.isValid() )
6425 {
6426 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( hue ).arg( saturation ).arg( value ).arg( alpha ) );
6427 return QVariant();
6428 }
6429
6430 return color;
6431}
6432
6433static QVariant fcnColorCmykF( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6434{
6435 const float cyan = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent ) ), 0.f, 1.f );
6436 const float magenta = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent ) ), 0.f, 1.f );
6437 const float yellow = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent ) ), 0.f, 1.f );
6438 const float black = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent ) ), 0.f, 1.f );
6439 const float alpha = std::clamp( static_cast<float>( QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent ) ), 0.f, 1.f );
6440
6441 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6442 if ( ! color.isValid() )
6443 {
6444 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6445 return QVariant();
6446 }
6447
6448 return color;
6449}
6450
6451static QVariant fcnColorCmyk( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6452{
6453 // Cyan ranges from 0 - 100
6454 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6455 // Magenta ranges from 0 - 100
6456 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6457 // Yellow ranges from 0 - 100
6458 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6459 // Black ranges from 0 - 100
6460 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6461
6462 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black );
6463
6464 if ( ! color.isValid() )
6465 {
6466 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ) );
6467 color = QColor( 0, 0, 0 );
6468 }
6469
6470 return QStringLiteral( "%1,%2,%3" ).arg( color.red() ).arg( color.green() ).arg( color.blue() );
6471}
6472
6473static QVariant fncColorCmyka( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6474{
6475 // Cyan ranges from 0 - 100
6476 double cyan = QgsExpressionUtils::getIntValue( values.at( 0 ), parent ) / 100.0;
6477 // Magenta ranges from 0 - 100
6478 double magenta = QgsExpressionUtils::getIntValue( values.at( 1 ), parent ) / 100.0;
6479 // Yellow ranges from 0 - 100
6480 double yellow = QgsExpressionUtils::getIntValue( values.at( 2 ), parent ) / 100.0;
6481 // Black ranges from 0 - 100
6482 double black = QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) / 100.0;
6483 // Alpha ranges from 0 - 255
6484 double alpha = QgsExpressionUtils::getIntValue( values.at( 4 ), parent ) / 255.0;
6485
6486 QColor color = QColor::fromCmykF( cyan, magenta, yellow, black, alpha );
6487 if ( ! color.isValid() )
6488 {
6489 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1:%2:%3:%4:%5' to color" ).arg( cyan ).arg( magenta ).arg( yellow ).arg( black ).arg( alpha ) );
6490 color = QColor( 0, 0, 0 );
6491 }
6492 return QgsSymbolLayerUtils::encodeColor( color );
6493}
6494
6495static QVariant fncColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6496{
6497 const QVariant variant = values.at( 0 );
6498 bool isQColor;
6499 const QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6500 if ( !color.isValid() )
6501 return QVariant();
6502
6503 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6504 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6505 return color.red();
6506 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6507 return color.green();
6508 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6509 return color.blue();
6510 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6511 return color.alpha();
6512 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6513 return static_cast< double >( color.hsvHueF() * 360 );
6514 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6515 return static_cast< double >( color.hsvSaturationF() * 100 );
6516 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6517 return static_cast< double >( color.valueF() * 100 );
6518 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6519 return static_cast< double >( color.hslHueF() * 360 );
6520 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6521 return static_cast< double >( color.hslSaturationF() * 100 );
6522 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6523 return static_cast< double >( color.lightnessF() * 100 );
6524 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6525 return static_cast< double >( color.cyanF() * 100 );
6526 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6527 return static_cast< double >( color.magentaF() * 100 );
6528 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6529 return static_cast< double >( color.yellowF() * 100 );
6530 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6531 return static_cast< double >( color.blackF() * 100 );
6532
6533 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6534 return QVariant();
6535}
6536
6537static QVariant fcnCreateRamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6538{
6539 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
6540 if ( map.empty() )
6541 {
6542 parent->setEvalErrorString( QObject::tr( "A minimum of two colors is required to create a ramp" ) );
6543 return QVariant();
6544 }
6545
6546 QList< QColor > colors;
6548 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
6549 {
6550 colors << QgsSymbolLayerUtils::decodeColor( it.value().toString() );
6551 if ( !colors.last().isValid() )
6552 {
6553 parent->setEvalErrorString( QObject::tr( "Cannot convert '%1' to color" ).arg( it.value().toString() ) );
6554 return QVariant();
6555 }
6556
6557 double step = it.key().toDouble();
6558 if ( it == map.constBegin() )
6559 {
6560 if ( step != 0.0 )
6561 stops << QgsGradientStop( step, colors.last() );
6562 }
6563 else if ( it == map.constEnd() )
6564 {
6565 if ( step != 1.0 )
6566 stops << QgsGradientStop( step, colors.last() );
6567 }
6568 else
6569 {
6570 stops << QgsGradientStop( step, colors.last() );
6571 }
6572 }
6573 bool discrete = values.at( 1 ).toBool();
6574
6575 if ( colors.empty() )
6576 return QVariant();
6577
6578 return QVariant::fromValue( QgsGradientColorRamp( colors.first(), colors.last(), discrete, stops ) );
6579}
6580
6581static QVariant fncSetColorPart( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6582{
6583 const QVariant variant = values.at( 0 );
6584 bool isQColor;
6585 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6586 if ( !color.isValid() )
6587 return QVariant();
6588
6589 QString part = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6590 int value = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
6591 if ( part.compare( QLatin1String( "red" ), Qt::CaseInsensitive ) == 0 )
6592 color.setRed( std::clamp( value, 0, 255 ) );
6593 else if ( part.compare( QLatin1String( "green" ), Qt::CaseInsensitive ) == 0 )
6594 color.setGreen( std::clamp( value, 0, 255 ) );
6595 else if ( part.compare( QLatin1String( "blue" ), Qt::CaseInsensitive ) == 0 )
6596 color.setBlue( std::clamp( value, 0, 255 ) );
6597 else if ( part.compare( QLatin1String( "alpha" ), Qt::CaseInsensitive ) == 0 )
6598 color.setAlpha( std::clamp( value, 0, 255 ) );
6599 else if ( part.compare( QLatin1String( "hue" ), Qt::CaseInsensitive ) == 0 )
6600 color.setHsv( std::clamp( value, 0, 359 ), color.hsvSaturation(), color.value(), color.alpha() );
6601 else if ( part.compare( QLatin1String( "saturation" ), Qt::CaseInsensitive ) == 0 )
6602 color.setHsvF( color.hsvHueF(), std::clamp( value, 0, 100 ) / 100.0, color.valueF(), color.alphaF() );
6603 else if ( part.compare( QLatin1String( "value" ), Qt::CaseInsensitive ) == 0 )
6604 color.setHsvF( color.hsvHueF(), color.hsvSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6605 else if ( part.compare( QLatin1String( "hsl_hue" ), Qt::CaseInsensitive ) == 0 )
6606 color.setHsl( std::clamp( value, 0, 359 ), color.hslSaturation(), color.lightness(), color.alpha() );
6607 else if ( part.compare( QLatin1String( "hsl_saturation" ), Qt::CaseInsensitive ) == 0 )
6608 color.setHslF( color.hslHueF(), std::clamp( value, 0, 100 ) / 100.0, color.lightnessF(), color.alphaF() );
6609 else if ( part.compare( QLatin1String( "lightness" ), Qt::CaseInsensitive ) == 0 )
6610 color.setHslF( color.hslHueF(), color.hslSaturationF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6611 else if ( part.compare( QLatin1String( "cyan" ), Qt::CaseInsensitive ) == 0 )
6612 color.setCmykF( std::clamp( value, 0, 100 ) / 100.0, color.magentaF(), color.yellowF(), color.blackF(), color.alphaF() );
6613 else if ( part.compare( QLatin1String( "magenta" ), Qt::CaseInsensitive ) == 0 )
6614 color.setCmykF( color.cyanF(), std::clamp( value, 0, 100 ) / 100.0, color.yellowF(), color.blackF(), color.alphaF() );
6615 else if ( part.compare( QLatin1String( "yellow" ), Qt::CaseInsensitive ) == 0 )
6616 color.setCmykF( color.cyanF(), color.magentaF(), std::clamp( value, 0, 100 ) / 100.0, color.blackF(), color.alphaF() );
6617 else if ( part.compare( QLatin1String( "black" ), Qt::CaseInsensitive ) == 0 )
6618 color.setCmykF( color.cyanF(), color.magentaF(), color.yellowF(), std::clamp( value, 0, 100 ) / 100.0, color.alphaF() );
6619 else
6620 {
6621 parent->setEvalErrorString( QObject::tr( "Unknown color component '%1'" ).arg( part ) );
6622 return QVariant();
6623 }
6624 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6625}
6626
6627static QVariant fncDarker( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6628{
6629 const QVariant variant = values.at( 0 );
6630 bool isQColor;
6631 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6632 if ( !color.isValid() )
6633 return QVariant();
6634
6635 color = color.darker( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6636
6637 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6638}
6639
6640static QVariant fncLighter( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6641{
6642 const QVariant variant = values.at( 0 );
6643 bool isQColor;
6644 QColor color = QgsExpressionUtils::getColorValue( variant, parent, isQColor );
6645 if ( !color.isValid() )
6646 return QVariant();
6647
6648 color = color.lighter( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ) );
6649
6650 return isQColor ? QVariant( color ) : QVariant( QgsSymbolLayerUtils::encodeColor( color ) );
6651}
6652
6653static QVariant fcnGetGeometry( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6654{
6655 QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6656 QgsGeometry geom = feat.geometry();
6657 if ( !geom.isNull() )
6658 return QVariant::fromValue( geom );
6659 return QVariant();
6660}
6661
6662static QVariant fcnGetFeatureId( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
6663{
6664 const QgsFeature feat = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
6665 if ( !feat.isValid() )
6666 return QVariant();
6667 return feat.id();
6668}
6669
6670static QVariant fcnTransformGeometry( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6671{
6672 QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
6673 QgsCoordinateReferenceSystem sCrs = QgsExpressionUtils::getCrsValue( values.at( 1 ), parent );
6674 QgsCoordinateReferenceSystem dCrs = QgsExpressionUtils::getCrsValue( values.at( 2 ), parent );
6675
6676 if ( !sCrs.isValid() )
6677 return QVariant::fromValue( fGeom );
6678
6679 if ( !dCrs.isValid() )
6680 return QVariant::fromValue( fGeom );
6681
6683 if ( context )
6684 tContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
6685 QgsCoordinateTransform t( sCrs, dCrs, tContext );
6686 try
6687 {
6689 return QVariant::fromValue( fGeom );
6690 }
6691 catch ( QgsCsException &cse )
6692 {
6693 QgsMessageLog::logMessage( QObject::tr( "Transform error caught in transform() function: %1" ).arg( cse.what() ) );
6694 return QVariant();
6695 }
6696 return QVariant();
6697}
6698
6699
6700static QVariant fcnGetFeatureById( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6701{
6702 bool foundLayer = false;
6703 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6704
6705 //no layer found
6706 if ( !featureSource || !foundLayer )
6707 {
6708 return QVariant();
6709 }
6710
6711 const QgsFeatureId fid = QgsExpressionUtils::getIntValue( values.at( 1 ), parent );
6712
6714 req.setFilterFid( fid );
6715 req.setTimeout( 10000 );
6716 req.setRequestMayBeNested( true );
6717 if ( context )
6718 req.setFeedback( context->feedback() );
6719 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6720
6721 QgsFeature fet;
6722 QVariant result;
6723 if ( fIt.nextFeature( fet ) )
6724 result = QVariant::fromValue( fet );
6725
6726 return result;
6727}
6728
6729static QVariant fcnGetFeature( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6730{
6731 //arguments: 1. layer id / name, 2. key attribute, 3. eq value
6732 bool foundLayer = false;
6733 std::unique_ptr<QgsVectorLayerFeatureSource> featureSource = QgsExpressionUtils::getFeatureSource( values.at( 0 ), context, parent, foundLayer );
6734
6735 //no layer found
6736 if ( !featureSource || !foundLayer )
6737 {
6738 return QVariant();
6739 }
6741 QString cacheValueKey;
6742 if ( values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
6743 {
6744 QVariantMap attributeMap = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
6745
6746 QMap <QString, QVariant>::const_iterator i = attributeMap.constBegin();
6747 QString filterString;
6748 for ( ; i != attributeMap.constEnd(); ++i )
6749 {
6750 if ( !filterString.isEmpty() )
6751 {
6752 filterString.append( " AND " );
6753 }
6754 filterString.append( QgsExpression::createFieldEqualityExpression( i.key(), i.value() ) );
6755 }
6756 cacheValueKey = QStringLiteral( "getfeature:%1:%2" ).arg( featureSource->id(), filterString );
6757 if ( context && context->hasCachedValue( cacheValueKey ) )
6758 {
6759 return context->cachedValue( cacheValueKey );
6760 }
6761 req.setFilterExpression( filterString );
6762 }
6763 else
6764 {
6765 QString attribute = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6766 int attributeId = featureSource->fields().lookupField( attribute );
6767 if ( attributeId == -1 )
6768 {
6769 return QVariant();
6770 }
6771
6772 const QVariant &attVal = values.at( 2 );
6773
6774 cacheValueKey = QStringLiteral( "getfeature:%1:%2:%3" ).arg( featureSource->id(), QString::number( attributeId ), attVal.toString() );
6775 if ( context && context->hasCachedValue( cacheValueKey ) )
6776 {
6777 return context->cachedValue( cacheValueKey );
6778 }
6779
6781 }
6782 req.setLimit( 1 );
6783 req.setTimeout( 10000 );
6784 req.setRequestMayBeNested( true );
6785 if ( context )
6786 req.setFeedback( context->feedback() );
6787 if ( !parent->needsGeometry() )
6788 {
6790 }
6791 QgsFeatureIterator fIt = featureSource->getFeatures( req );
6792
6793 QgsFeature fet;
6794 QVariant res;
6795 if ( fIt.nextFeature( fet ) )
6796 {
6797 res = QVariant::fromValue( fet );
6798 }
6799
6800 if ( context )
6801 context->setCachedValue( cacheValueKey, res );
6802 return res;
6803}
6804
6805static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
6806{
6807 QVariant result;
6808 QString fieldName;
6809
6810 if ( context )
6811 {
6812 if ( !values.isEmpty() )
6813 {
6814 QgsExpressionNodeColumnRef *col = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
6815 if ( col && ( values.size() == 1 || !values.at( 1 ).isValid() ) )
6816 fieldName = col->name();
6817 else if ( values.size() == 2 )
6818 fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6819 }
6820
6821 QVariant value = values.at( 0 );
6822
6823 const QgsFields fields = context->fields();
6824 int fieldIndex = fields.lookupField( fieldName );
6825
6826 if ( fieldIndex == -1 )
6827 {
6828 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6829 }
6830 else
6831 {
6832 // TODO this function is NOT thread safe
6834 QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), context, parent );
6836
6837 const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName, value.toString() );
6838 if ( context->hasCachedValue( cacheValueKey ) )
6839 {
6840 return context->cachedValue( cacheValueKey );
6841 }
6842
6843 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
6845
6846 const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );
6847
6848 QVariant cache;
6849 if ( !context->hasCachedValue( cacheKey ) )
6850 {
6851 cache = formatter->createCache( layer, fieldIndex, setup.config() );
6852 context->setCachedValue( cacheKey, cache );
6853 }
6854 else
6855 cache = context->cachedValue( cacheKey );
6856
6857 result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
6858
6859 context->setCachedValue( cacheValueKey, result );
6860 }
6861 }
6862 else
6863 {
6864 parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: function cannot be evaluated without a context." ).arg( QStringLiteral( "represent_value" ), fieldName ) );
6865 }
6866
6867 return result;
6868}
6869
6870static QVariant fcnMimeType( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
6871{
6872 const QVariant data = values.at( 0 );
6873 const QMimeDatabase db;
6874 return db.mimeTypeForData( data.toByteArray() ).name();
6875}
6876
6877static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6878{
6879 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
6880
6881 bool foundLayer = false;
6882 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [layerProperty]( QgsMapLayer * layer )-> QVariant
6883 {
6884 if ( !layer )
6885 return QVariant();
6886
6887 // here, we always prefer the layer metadata values over the older server-specific published values
6888 if ( QString::compare( layerProperty, QStringLiteral( "name" ), Qt::CaseInsensitive ) == 0 )
6889 return layer->name();
6890 else if ( QString::compare( layerProperty, QStringLiteral( "id" ), Qt::CaseInsensitive ) == 0 )
6891 return layer->id();
6892 else if ( QString::compare( layerProperty, QStringLiteral( "title" ), Qt::CaseInsensitive ) == 0 )
6893 return !layer->metadata().title().isEmpty() ? layer->metadata().title() : layer->serverProperties()->title();
6894 else if ( QString::compare( layerProperty, QStringLiteral( "abstract" ), Qt::CaseInsensitive ) == 0 )
6895 return !layer->metadata().abstract().isEmpty() ? layer->metadata().abstract() : layer->serverProperties()->abstract();
6896 else if ( QString::compare( layerProperty, QStringLiteral( "keywords" ), Qt::CaseInsensitive ) == 0 )
6897 {
6898 QStringList keywords;
6899 const QgsAbstractMetadataBase::KeywordMap keywordMap = layer->metadata().keywords();
6900 for ( auto it = keywordMap.constBegin(); it != keywordMap.constEnd(); ++it )
6901 {
6902 keywords.append( it.value() );
6903 }
6904 if ( !keywords.isEmpty() )
6905 return keywords;
6906 return layer->serverProperties()->keywordList();
6907 }
6908 else if ( QString::compare( layerProperty, QStringLiteral( "data_url" ), Qt::CaseInsensitive ) == 0 )
6909 return layer->serverProperties()->dataUrl();
6910 else if ( QString::compare( layerProperty, QStringLiteral( "attribution" ), Qt::CaseInsensitive ) == 0 )
6911 {
6912 return !layer->metadata().rights().isEmpty() ? QVariant( layer->metadata().rights() ) : QVariant( layer->serverProperties()->attribution() );
6913 }
6914 else if ( QString::compare( layerProperty, QStringLiteral( "attribution_url" ), Qt::CaseInsensitive ) == 0 )
6915 return layer->serverProperties()->attributionUrl();
6916 else if ( QString::compare( layerProperty, QStringLiteral( "source" ), Qt::CaseInsensitive ) == 0 )
6917 return layer->publicSource();
6918 else if ( QString::compare( layerProperty, QStringLiteral( "min_scale" ), Qt::CaseInsensitive ) == 0 )
6919 return layer->minimumScale();
6920 else if ( QString::compare( layerProperty, QStringLiteral( "max_scale" ), Qt::CaseInsensitive ) == 0 )
6921 return layer->maximumScale();
6922 else if ( QString::compare( layerProperty, QStringLiteral( "is_editable" ), Qt::CaseInsensitive ) == 0 )
6923 return layer->isEditable();
6924 else if ( QString::compare( layerProperty, QStringLiteral( "crs" ), Qt::CaseInsensitive ) == 0 )
6925 return layer->crs().authid();
6926 else if ( QString::compare( layerProperty, QStringLiteral( "crs_definition" ), Qt::CaseInsensitive ) == 0 )
6927 return layer->crs().toProj();
6928 else if ( QString::compare( layerProperty, QStringLiteral( "crs_description" ), Qt::CaseInsensitive ) == 0 )
6929 return layer->crs().description();
6930 else if ( QString::compare( layerProperty, QStringLiteral( "crs_ellipsoid" ), Qt::CaseInsensitive ) == 0 )
6931 return layer->crs().ellipsoidAcronym();
6932 else if ( QString::compare( layerProperty, QStringLiteral( "extent" ), Qt::CaseInsensitive ) == 0 )
6933 {
6934 QgsGeometry extentGeom = QgsGeometry::fromRect( layer->extent() );
6935 QVariant result = QVariant::fromValue( extentGeom );
6936 return result;
6937 }
6938 else if ( QString::compare( layerProperty, QStringLiteral( "distance_units" ), Qt::CaseInsensitive ) == 0 )
6939 return QgsUnitTypes::encodeUnit( layer->crs().mapUnits() );
6940 else if ( QString::compare( layerProperty, QStringLiteral( "path" ), Qt::CaseInsensitive ) == 0 )
6941 {
6942 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
6943 return decodedUri.value( QStringLiteral( "path" ) );
6944 }
6945 else if ( QString::compare( layerProperty, QStringLiteral( "type" ), Qt::CaseInsensitive ) == 0 )
6946 {
6947 switch ( layer->type() )
6948 {
6950 return QCoreApplication::translate( "expressions", "Vector" );
6952 return QCoreApplication::translate( "expressions", "Raster" );
6954 return QCoreApplication::translate( "expressions", "Mesh" );
6956 return QCoreApplication::translate( "expressions", "Vector Tile" );
6958 return QCoreApplication::translate( "expressions", "Plugin" );
6960 return QCoreApplication::translate( "expressions", "Annotation" );
6962 return QCoreApplication::translate( "expressions", "Point Cloud" );
6964 return QCoreApplication::translate( "expressions", "Group" );
6966 return QCoreApplication::translate( "expressions", "Tiled Scene" );
6967 }
6968 }
6969 else
6970 {
6971 //vector layer methods
6972 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
6973 if ( vLayer )
6974 {
6975 if ( QString::compare( layerProperty, QStringLiteral( "storage_type" ), Qt::CaseInsensitive ) == 0 )
6976 return vLayer->storageType();
6977 else if ( QString::compare( layerProperty, QStringLiteral( "geometry_type" ), Qt::CaseInsensitive ) == 0 )
6979 else if ( QString::compare( layerProperty, QStringLiteral( "feature_count" ), Qt::CaseInsensitive ) == 0 )
6980 return QVariant::fromValue( vLayer->featureCount() );
6981 }
6982 }
6983
6984 return QVariant();
6985 }, foundLayer );
6986
6987 if ( !foundLayer )
6988 return QVariant();
6989 else
6990 return res;
6991}
6992
6993static QVariant fcnDecodeUri( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
6994{
6995 const QString uriPart = values.at( 1 ).toString();
6996
6997 bool foundLayer = false;
6998
6999 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, uriPart]( QgsMapLayer * layer )-> QVariant
7000 {
7001 if ( !layer->dataProvider() )
7002 {
7003 parent->setEvalErrorString( QObject::tr( "Layer %1 has invalid data provider" ).arg( layer->name() ) );
7004 return QVariant();
7005 }
7006
7007 const QVariantMap decodedUri = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->dataProvider()->dataSourceUri() );
7008
7009 if ( !uriPart.isNull() )
7010 {
7011 return decodedUri.value( uriPart );
7012 }
7013 else
7014 {
7015 return decodedUri;
7016 }
7017 }, foundLayer );
7018
7019 if ( !foundLayer )
7020 {
7021 parent->setEvalErrorString( QObject::tr( "Function `decode_uri` requires a valid layer." ) );
7022 return QVariant();
7023 }
7024 else
7025 {
7026 return res;
7027 }
7028}
7029
7030static QVariant fcnGetRasterBandStat( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7031{
7032 const int band = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7033 const QString layerProperty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7034
7035 bool foundLayer = false;
7036 const QVariant res = QgsExpressionUtils::runMapLayerFunctionThreadSafe( values.at( 0 ), context, parent, [parent, band, layerProperty]( QgsMapLayer * layer )-> QVariant
7037 {
7038 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( layer );
7039 if ( !rl )
7040 return QVariant();
7041
7042 if ( band < 1 || band > rl->bandCount() )
7043 {
7044 parent->setEvalErrorString( QObject::tr( "Invalid band number %1 for layer" ).arg( band ) );
7045 return QVariant();
7046 }
7047
7049
7050 if ( QString::compare( layerProperty, QStringLiteral( "avg" ), Qt::CaseInsensitive ) == 0 )
7052 else if ( QString::compare( layerProperty, QStringLiteral( "stdev" ), Qt::CaseInsensitive ) == 0 )
7054 else if ( QString::compare( layerProperty, QStringLiteral( "min" ), Qt::CaseInsensitive ) == 0 )
7056 else if ( QString::compare( layerProperty, QStringLiteral( "max" ), Qt::CaseInsensitive ) == 0 )
7058 else if ( QString::compare( layerProperty, QStringLiteral( "range" ), Qt::CaseInsensitive ) == 0 )
7060 else if ( QString::compare( layerProperty, QStringLiteral( "sum" ), Qt::CaseInsensitive ) == 0 )
7062 else
7063 {
7064 parent->setEvalErrorString( QObject::tr( "Invalid raster statistic: '%1'" ).arg( layerProperty ) );
7065 return QVariant();
7066 }
7067
7068 QgsRasterBandStats stats = rl->dataProvider()->bandStatistics( band, stat );
7069 switch ( stat )
7070 {
7072 return stats.mean;
7074 return stats.stdDev;
7076 return stats.minimumValue;
7078 return stats.maximumValue;
7080 return stats.range;
7082 return stats.sum;
7083 default:
7084 break;
7085 }
7086 return QVariant();
7087 }, foundLayer );
7088
7089 if ( !foundLayer )
7090 {
7091#if 0 // for consistency with other functions we should raise an error here, but for compatibility with old projects we don't
7092 parent->setEvalErrorString( QObject::tr( "Function `raster_statistic` requires a valid raster layer." ) );
7093#endif
7094 return QVariant();
7095 }
7096 else
7097 {
7098 return res;
7099 }
7100}
7101
7102static QVariant fcnArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7103{
7104 return values;
7105}
7106
7107static QVariant fcnArraySort( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7108{
7109 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7110 bool ascending = values.value( 1 ).toBool();
7111 std::sort( list.begin(), list.end(), [ascending]( QVariant a, QVariant b ) -> bool { return ( !ascending ? qgsVariantLessThan( b, a ) : qgsVariantLessThan( a, b ) ); } );
7112 return list;
7113}
7114
7115static QVariant fcnArrayLength( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7116{
7117 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).length();
7118}
7119
7120static QVariant fcnArrayContains( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7121{
7122 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).contains( values.at( 1 ) ) );
7123}
7124
7125static QVariant fcnArrayCount( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7126{
7127 return QVariant( QgsExpressionUtils::getListValue( values.at( 0 ), parent ).count( values.at( 1 ) ) );
7128}
7129
7130static QVariant fcnArrayAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7131{
7132 QVariantList listA = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7133 QVariantList listB = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7134 int match = 0;
7135 for ( const auto &item : listB )
7136 {
7137 if ( listA.contains( item ) )
7138 match++;
7139 }
7140
7141 return QVariant( match == listB.count() );
7142}
7143
7144static QVariant fcnArrayFind( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7145{
7146 return QgsExpressionUtils::getListValue( values.at( 0 ), parent ).indexOf( values.at( 1 ) );
7147}
7148
7149static QVariant fcnArrayGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7150{
7151 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7152 const int pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7153 if ( pos < list.length() && pos >= 0 ) return list.at( pos );
7154 else if ( pos < 0 && ( list.length() + pos ) >= 0 )
7155 return list.at( list.length() + pos );
7156 return QVariant();
7157}
7158
7159static QVariant fcnArrayFirst( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7160{
7161 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7162 return list.value( 0 );
7163}
7164
7165static QVariant fcnArrayLast( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7166{
7167 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7168 return list.value( list.size() - 1 );
7169}
7170
7171static QVariant fcnArrayMinimum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7172{
7173 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7174 return list.isEmpty() ? QVariant() : *std::min_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7175}
7176
7177static QVariant fcnArrayMaximum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7178{
7179 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7180 return list.isEmpty() ? QVariant() : *std::max_element( list.constBegin(), list.constEnd(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7181}
7182
7183static QVariant fcnArrayMean( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7184{
7185 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7186 int i = 0;
7187 double total = 0.0;
7188 for ( const QVariant &item : list )
7189 {
7190 switch ( item.userType() )
7191 {
7192 case QMetaType::Int:
7193 case QMetaType::UInt:
7194 case QMetaType::LongLong:
7195 case QMetaType::ULongLong:
7196 case QMetaType::Float:
7197 case QMetaType::Double:
7198 total += item.toDouble();
7199 ++i;
7200 break;
7201 }
7202 }
7203 return i == 0 ? QVariant() : total / i;
7204}
7205
7206static QVariant fcnArrayMedian( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7207{
7208 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7209 QVariantList numbers;
7210 for ( const auto &item : list )
7211 {
7212 switch ( item.userType() )
7213 {
7214 case QMetaType::Int:
7215 case QMetaType::UInt:
7216 case QMetaType::LongLong:
7217 case QMetaType::ULongLong:
7218 case QMetaType::Float:
7219 case QMetaType::Double:
7220 numbers.append( item );
7221 break;
7222 }
7223 }
7224 std::sort( numbers.begin(), numbers.end(), []( QVariant a, QVariant b ) -> bool { return ( qgsVariantLessThan( a, b ) ); } );
7225 const int count = numbers.count();
7226 if ( count == 0 )
7227 {
7228 return QVariant();
7229 }
7230 else if ( count % 2 )
7231 {
7232 return numbers.at( count / 2 );
7233 }
7234 else
7235 {
7236 return ( numbers.at( count / 2 - 1 ).toDouble() + numbers.at( count / 2 ).toDouble() ) / 2;
7237 }
7238}
7239
7240static QVariant fcnArraySum( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7241{
7242 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7243 int i = 0;
7244 double total = 0.0;
7245 for ( const QVariant &item : list )
7246 {
7247 switch ( item.userType() )
7248 {
7249 case QMetaType::Int:
7250 case QMetaType::UInt:
7251 case QMetaType::LongLong:
7252 case QMetaType::ULongLong:
7253 case QMetaType::Float:
7254 case QMetaType::Double:
7255 total += item.toDouble();
7256 ++i;
7257 break;
7258 }
7259 }
7260 return i == 0 ? QVariant() : total;
7261}
7262
7263static QVariant convertToSameType( const QVariant &value, QMetaType::Type type )
7264{
7265 QVariant result = value;
7266 result.convert( static_cast<int>( type ) );
7267 return result;
7268}
7269
7270static QVariant fcnArrayMajority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7271{
7272 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7273 QHash< QVariant, int > hash;
7274 for ( const auto &item : list )
7275 {
7276 ++hash[item];
7277 }
7278 const QList< int > occurrences = hash.values();
7279 if ( occurrences.empty() )
7280 return QVariantList();
7281
7282 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7283
7284 const QString option = values.at( 1 ).toString();
7285 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7286 {
7287 return convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7288 }
7289 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7290 {
7291 if ( hash.isEmpty() )
7292 return QVariant();
7293
7294 return QVariant( hash.key( maxValue ) );
7295 }
7296 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7297 {
7298 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( maxValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7299 }
7300 else if ( option.compare( QLatin1String( "real_majority" ), Qt::CaseInsensitive ) == 0 )
7301 {
7302 if ( maxValue * 2 <= list.size() )
7303 return QVariant();
7304
7305 return QVariant( hash.key( maxValue ) );
7306 }
7307 else
7308 {
7309 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7310 return QVariant();
7311 }
7312}
7313
7314static QVariant fcnArrayMinority( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
7315{
7316 const QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7317 QHash< QVariant, int > hash;
7318 for ( const auto &item : list )
7319 {
7320 ++hash[item];
7321 }
7322 const QList< int > occurrences = hash.values();
7323 if ( occurrences.empty() )
7324 return QVariantList();
7325
7326 const int minValue = *std::min_element( occurrences.constBegin(), occurrences.constEnd() );
7327
7328 const QString option = values.at( 1 ).toString();
7329 if ( option.compare( QLatin1String( "all" ), Qt::CaseInsensitive ) == 0 )
7330 {
7331 return convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7332 }
7333 else if ( option.compare( QLatin1String( "any" ), Qt::CaseInsensitive ) == 0 )
7334 {
7335 if ( hash.isEmpty() )
7336 return QVariant();
7337
7338 return QVariant( hash.key( minValue ) );
7339 }
7340 else if ( option.compare( QLatin1String( "median" ), Qt::CaseInsensitive ) == 0 )
7341 {
7342 return fcnArrayMedian( QVariantList() << convertToSameType( hash.keys( minValue ), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) ), context, parent, node );
7343 }
7344 else if ( option.compare( QLatin1String( "real_minority" ), Qt::CaseInsensitive ) == 0 )
7345 {
7346 if ( hash.isEmpty() )
7347 return QVariant();
7348
7349 // Remove the majority, all others are minority
7350 const int maxValue = *std::max_element( occurrences.constBegin(), occurrences.constEnd() );
7351 if ( maxValue * 2 > list.size() )
7352 hash.remove( hash.key( maxValue ) );
7353
7354 return convertToSameType( hash.keys(), static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7355 }
7356 else
7357 {
7358 parent->setEvalErrorString( QObject::tr( "No such option '%1'" ).arg( option ) );
7359 return QVariant();
7360 }
7361}
7362
7363static QVariant fcnArrayAppend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7364{
7365 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7366 list.append( values.at( 1 ) );
7367 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7368}
7369
7370static QVariant fcnArrayPrepend( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7371{
7372 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7373 list.prepend( values.at( 1 ) );
7374 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7375}
7376
7377static QVariant fcnArrayInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7378{
7379 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7380 list.insert( QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent ), values.at( 2 ) );
7381 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7382}
7383
7384static QVariant fcnArrayRemoveAt( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7385{
7386 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7387 int position = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7388 if ( position < 0 )
7389 position = position + list.length();
7390 if ( position >= 0 && position < list.length() )
7391 list.removeAt( position );
7392 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7393}
7394
7395static QVariant fcnArrayRemoveAll( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7396{
7397 if ( QgsVariantUtils::isNull( values.at( 0 ) ) )
7398 return QVariant();
7399
7400 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7401
7402 const QVariant toRemove = values.at( 1 );
7403 if ( QgsVariantUtils::isNull( toRemove ) )
7404 {
7405 list.erase( std::remove_if( list.begin(), list.end(), []( const QVariant & element )
7406 {
7407 return QgsVariantUtils::isNull( element );
7408 } ), list.end() );
7409 }
7410 else
7411 {
7412 list.removeAll( toRemove );
7413 }
7414 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7415}
7416
7417static QVariant fcnArrayReplace( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7418{
7419 if ( values.count() == 2 && values.at( 1 ).userType() == QMetaType::Type::QVariantMap )
7420 {
7421 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 1 ), parent );
7422
7423 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7424 for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it )
7425 {
7426 int index = list.indexOf( it.key() );
7427 while ( index >= 0 )
7428 {
7429 list.replace( index, it.value() );
7430 index = list.indexOf( it.key() );
7431 }
7432 }
7433
7434 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7435 }
7436 else if ( values.count() == 3 )
7437 {
7438 QVariantList before;
7439 QVariantList after;
7440 bool isSingleReplacement = false;
7441
7442 if ( !QgsExpressionUtils::isList( values.at( 1 ) ) && values.at( 2 ).userType() != QMetaType::Type::QStringList )
7443 {
7444 before = QVariantList() << values.at( 1 );
7445 }
7446 else
7447 {
7448 before = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7449 }
7450
7451 if ( !QgsExpressionUtils::isList( values.at( 2 ) ) )
7452 {
7453 after = QVariantList() << values.at( 2 );
7454 isSingleReplacement = true;
7455 }
7456 else
7457 {
7458 after = QgsExpressionUtils::getListValue( values.at( 2 ), parent );
7459 }
7460
7461 if ( !isSingleReplacement && before.length() != after.length() )
7462 {
7463 parent->setEvalErrorString( QObject::tr( "Invalid pair of array, length not identical" ) );
7464 return QVariant();
7465 }
7466
7467 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7468 for ( int i = 0; i < before.length(); i++ )
7469 {
7470 int index = list.indexOf( before.at( i ) );
7471 while ( index >= 0 )
7472 {
7473 list.replace( index, after.at( isSingleReplacement ? 0 : i ) );
7474 index = list.indexOf( before.at( i ) );
7475 }
7476 }
7477
7478 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7479 }
7480 else
7481 {
7482 parent->setEvalErrorString( QObject::tr( "Function array_replace requires 2 or 3 arguments" ) );
7483 return QVariant();
7484 }
7485}
7486
7487static QVariant fcnArrayPrioritize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7488{
7489 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7490 QVariantList list_new;
7491
7492 for ( const QVariant &cur : QgsExpressionUtils::getListValue( values.at( 1 ), parent ) )
7493 {
7494 while ( list.removeOne( cur ) )
7495 {
7496 list_new.append( cur );
7497 }
7498 }
7499
7500 list_new.append( list );
7501
7502 return convertToSameType( list_new, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7503}
7504
7505static QVariant fcnArrayCat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7506{
7507 QVariantList list;
7508 for ( const QVariant &cur : values )
7509 {
7510 list += QgsExpressionUtils::getListValue( cur, parent );
7511 }
7512 return convertToSameType( list, static_cast<QMetaType::Type>( values.at( 0 ).userType() ) );
7513}
7514
7515static QVariant fcnArraySlice( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7516{
7517 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7518 int start_pos = QgsExpressionUtils::getNativeIntValue( values.at( 1 ), parent );
7519 const int end_pos = QgsExpressionUtils::getNativeIntValue( values.at( 2 ), parent );
7520 int slice_length = 0;
7521 // negative positions means positions taken relative to the end of the array
7522 if ( start_pos < 0 )
7523 {
7524 start_pos = list.length() + start_pos;
7525 }
7526 if ( end_pos >= 0 )
7527 {
7528 slice_length = end_pos - start_pos + 1;
7529 }
7530 else
7531 {
7532 slice_length = list.length() + end_pos - start_pos + 1;
7533 }
7534 //avoid negative lengths in QList.mid function
7535 if ( slice_length < 0 )
7536 {
7537 slice_length = 0;
7538 }
7539 list = list.mid( start_pos, slice_length );
7540 return list;
7541}
7542
7543static QVariant fcnArrayReverse( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7544{
7545 QVariantList list = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7546 std::reverse( list.begin(), list.end() );
7547 return list;
7548}
7549
7550static QVariant fcnArrayIntersect( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7551{
7552 const QVariantList array1 = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7553 const QVariantList array2 = QgsExpressionUtils::getListValue( values.at( 1 ), parent );
7554 for ( const QVariant &cur : array2 )
7555 {
7556 if ( array1.contains( cur ) )
7557 return QVariant( true );
7558 }
7559 return QVariant( false );
7560}
7561
7562static QVariant fcnArrayDistinct( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7563{
7564 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7565
7566 QVariantList distinct;
7567
7568 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7569 {
7570 if ( !distinct.contains( *it ) )
7571 {
7572 distinct += ( *it );
7573 }
7574 }
7575
7576 return distinct;
7577}
7578
7579static QVariant fcnArrayToString( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7580{
7581 QVariantList array = QgsExpressionUtils::getListValue( values.at( 0 ), parent );
7582 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7583 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7584
7585 QString str;
7586
7587 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it )
7588 {
7589 str += ( !( *it ).toString().isEmpty() ) ? ( *it ).toString() : empty;
7590 if ( it != ( array.constEnd() - 1 ) )
7591 {
7592 str += delimiter;
7593 }
7594 }
7595
7596 return QVariant( str );
7597}
7598
7599static QVariant fcnStringToArray( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7600{
7601 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7602 QString delimiter = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7603 QString empty = QgsExpressionUtils::getStringValue( values.at( 2 ), parent );
7604
7605 QStringList list = str.split( delimiter );
7606 QVariantList array;
7607
7608 for ( QStringList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
7609 {
7610 array += ( !( *it ).isEmpty() ) ? *it : empty;
7611 }
7612
7613 return array;
7614}
7615
7616static QVariant fcnLoadJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7617{
7618 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7619 QJsonDocument document = QJsonDocument::fromJson( str.toUtf8() );
7620 if ( document.isNull() )
7621 return QVariant();
7622
7623 return document.toVariant();
7624}
7625
7626static QVariant fcnWriteJson( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7627{
7628 Q_UNUSED( parent )
7629 QJsonDocument document = QJsonDocument::fromVariant( values.at( 0 ) );
7630 return QString( document.toJson( QJsonDocument::Compact ) );
7631}
7632
7633static QVariant fcnHstoreToMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7634{
7635 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7636 if ( str.isEmpty() )
7637 return QVariantMap();
7638 str = str.trimmed();
7639
7640 return QgsHstoreUtils::parse( str );
7641}
7642
7643static QVariant fcnMapToHstore( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7644{
7645 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7646 return QgsHstoreUtils::build( map );
7647}
7648
7649static QVariant fcnMap( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7650{
7651 QVariantMap result;
7652 for ( int i = 0; i + 1 < values.length(); i += 2 )
7653 {
7654 result.insert( QgsExpressionUtils::getStringValue( values.at( i ), parent ), values.at( i + 1 ) );
7655 }
7656 return result;
7657}
7658
7659static QVariant fcnMapPrefixKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7660{
7661 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7662 const QString prefix = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
7663 QVariantMap resultMap;
7664
7665 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7666 {
7667 resultMap.insert( QString( it.key() ).prepend( prefix ), it.value() );
7668 }
7669
7670 return resultMap;
7671}
7672
7673static QVariant fcnMapGet( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7674{
7675 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).value( values.at( 1 ).toString() );
7676}
7677
7678static QVariant fcnMapExist( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7679{
7680 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).contains( values.at( 1 ).toString() );
7681}
7682
7683static QVariant fcnMapDelete( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7684{
7685 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7686 map.remove( values.at( 1 ).toString() );
7687 return map;
7688}
7689
7690static QVariant fcnMapInsert( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7691{
7692 QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7693 map.insert( values.at( 1 ).toString(), values.at( 2 ) );
7694 return map;
7695}
7696
7697static QVariant fcnMapConcat( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7698{
7699 QVariantMap result;
7700 for ( const QVariant &cur : values )
7701 {
7702 const QVariantMap curMap = QgsExpressionUtils::getMapValue( cur, parent );
7703 for ( QVariantMap::const_iterator it = curMap.constBegin(); it != curMap.constEnd(); ++it )
7704 result.insert( it.key(), it.value() );
7705 }
7706 return result;
7707}
7708
7709static QVariant fcnMapAKeys( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7710{
7711 return QStringList( QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).keys() );
7712}
7713
7714static QVariant fcnMapAVals( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7715{
7716 return QgsExpressionUtils::getMapValue( values.at( 0 ), parent ).values();
7717}
7718
7719static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7720{
7721 const QString envVarName = values.at( 0 ).toString();
7722 if ( !QProcessEnvironment::systemEnvironment().contains( envVarName ) )
7723 return QVariant();
7724
7725 return QProcessEnvironment::systemEnvironment().value( envVarName );
7726}
7727
7728static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7729{
7730 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7731 if ( parent->hasEvalError() )
7732 {
7733 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "base_file_name" ) ) );
7734 return QVariant();
7735 }
7736 return QFileInfo( file ).completeBaseName();
7737}
7738
7739static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7740{
7741 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7742 if ( parent->hasEvalError() )
7743 {
7744 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_suffix" ) ) );
7745 return QVariant();
7746 }
7747 return QFileInfo( file ).completeSuffix();
7748}
7749
7750static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7751{
7752 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7753 if ( parent->hasEvalError() )
7754 {
7755 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_exists" ) ) );
7756 return QVariant();
7757 }
7758 return QFileInfo::exists( file );
7759}
7760
7761static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7762{
7763 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7764 if ( parent->hasEvalError() )
7765 {
7766 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_name" ) ) );
7767 return QVariant();
7768 }
7769 return QFileInfo( file ).fileName();
7770}
7771
7772static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7773{
7774 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7775 if ( parent->hasEvalError() )
7776 {
7777 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_file" ) ) );
7778 return QVariant();
7779 }
7780 return QFileInfo( file ).isFile();
7781}
7782
7783static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7784{
7785 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7786 if ( parent->hasEvalError() )
7787 {
7788 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "is_directory" ) ) );
7789 return QVariant();
7790 }
7791 return QFileInfo( file ).isDir();
7792}
7793
7794static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7795{
7796 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7797 if ( parent->hasEvalError() )
7798 {
7799 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_path" ) ) );
7800 return QVariant();
7801 }
7802 return QDir::toNativeSeparators( QFileInfo( file ).path() );
7803}
7804
7805static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
7806{
7807 const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), context, parent );
7808 if ( parent->hasEvalError() )
7809 {
7810 parent->setEvalErrorString( QObject::tr( "Function `%1` requires a value which represents a possible file path" ).arg( QLatin1String( "file_size" ) ) );
7811 return QVariant();
7812 }
7813 return QFileInfo( file ).size();
7814}
7815
7816static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
7817{
7818 return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
7819}
7820
7821static QVariant fcnGenericHash( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7822{
7823 QVariant hash;
7824 QString str = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7825 QString method = QgsExpressionUtils::getStringValue( values.at( 1 ), parent ).toLower();
7826
7827 if ( method == QLatin1String( "md4" ) )
7828 {
7829 hash = fcnHash( str, QCryptographicHash::Md4 );
7830 }
7831 else if ( method == QLatin1String( "md5" ) )
7832 {
7833 hash = fcnHash( str, QCryptographicHash::Md5 );
7834 }
7835 else if ( method == QLatin1String( "sha1" ) )
7836 {
7837 hash = fcnHash( str, QCryptographicHash::Sha1 );
7838 }
7839 else if ( method == QLatin1String( "sha224" ) )
7840 {
7841 hash = fcnHash( str, QCryptographicHash::Sha224 );
7842 }
7843 else if ( method == QLatin1String( "sha256" ) )
7844 {
7845 hash = fcnHash( str, QCryptographicHash::Sha256 );
7846 }
7847 else if ( method == QLatin1String( "sha384" ) )
7848 {
7849 hash = fcnHash( str, QCryptographicHash::Sha384 );
7850 }
7851 else if ( method == QLatin1String( "sha512" ) )
7852 {
7853 hash = fcnHash( str, QCryptographicHash::Sha512 );
7854 }
7855 else if ( method == QLatin1String( "sha3_224" ) )
7856 {
7857 hash = fcnHash( str, QCryptographicHash::Sha3_224 );
7858 }
7859 else if ( method == QLatin1String( "sha3_256" ) )
7860 {
7861 hash = fcnHash( str, QCryptographicHash::Sha3_256 );
7862 }
7863 else if ( method == QLatin1String( "sha3_384" ) )
7864 {
7865 hash = fcnHash( str, QCryptographicHash::Sha3_384 );
7866 }
7867 else if ( method == QLatin1String( "sha3_512" ) )
7868 {
7869 hash = fcnHash( str, QCryptographicHash::Sha3_512 );
7870 }
7871 else if ( method == QLatin1String( "keccak_224" ) )
7872 {
7873 hash = fcnHash( str, QCryptographicHash::Keccak_224 );
7874 }
7875 else if ( method == QLatin1String( "keccak_256" ) )
7876 {
7877 hash = fcnHash( str, QCryptographicHash::Keccak_256 );
7878 }
7879 else if ( method == QLatin1String( "keccak_384" ) )
7880 {
7881 hash = fcnHash( str, QCryptographicHash::Keccak_384 );
7882 }
7883 else if ( method == QLatin1String( "keccak_512" ) )
7884 {
7885 hash = fcnHash( str, QCryptographicHash::Keccak_512 );
7886 }
7887 else
7888 {
7889 parent->setEvalErrorString( QObject::tr( "Hash method %1 is not available on this system." ).arg( str ) );
7890 }
7891 return hash;
7892}
7893
7894static QVariant fcnHashMd5( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7895{
7896 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Md5 );
7897}
7898
7899static QVariant fcnHashSha256( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7900{
7901 return fcnHash( QgsExpressionUtils::getStringValue( values.at( 0 ), parent ), QCryptographicHash::Sha256 );
7902}
7903
7904static QVariant fcnToBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
7905{
7906 const QByteArray input = values.at( 0 ).toByteArray();
7907 return QVariant( QString( input.toBase64() ) );
7908}
7909
7910static QVariant fcnToFormUrlEncode( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7911{
7912 const QVariantMap map = QgsExpressionUtils::getMapValue( values.at( 0 ), parent );
7913 QUrlQuery query;
7914 for ( auto it = map.cbegin(); it != map.cend(); it++ )
7915 {
7916 query.addQueryItem( it.key(), it.value().toString() );
7917 }
7918 return query.toString( QUrl::ComponentFormattingOption::FullyEncoded );
7919}
7920
7921static QVariant fcnFromBase64( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
7922{
7923 const QString value = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
7924 const QByteArray base64 = value.toLocal8Bit();
7925 const QByteArray decoded = QByteArray::fromBase64( base64 );
7926 return QVariant( decoded );
7927}
7928
7929typedef bool ( QgsGeometry::*RelationFunction )( const QgsGeometry &geometry ) const;
7930
7931static 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 )
7932{
7933
7934 if ( ! context )
7935 {
7936 parent->setEvalErrorString( QStringLiteral( "This function was called without an expression context." ) );
7937 return QVariant();
7938 }
7939
7940 const QVariant sourceLayerRef = context->variable( QStringLiteral( "layer" ) ); //used to detect if sourceLayer and targetLayer are the same
7941 // TODO this function is NOT thread safe
7943 QgsVectorLayer *sourceLayer = QgsExpressionUtils::getVectorLayer( sourceLayerRef, context, parent );
7945
7946 QgsFeatureRequest request;
7947 request.setTimeout( 10000 );
7948 request.setRequestMayBeNested( true );
7949 request.setFeedback( context->feedback() );
7950
7951 // First parameter is the overlay layer
7952 QgsExpressionNode *node = QgsExpressionUtils::getNode( values.at( 0 ), parent );
7954
7955 const bool layerCanBeCached = node->isStatic( parent, context );
7956 QVariant targetLayerValue = node->eval( parent, context );
7958
7959 // Second parameter is the expression to evaluate (or null for testonly)
7960 node = QgsExpressionUtils::getNode( values.at( 1 ), parent );
7962 QString subExpString = node->dump();
7963
7964 bool testOnly = ( subExpString == "NULL" );
7965 // TODO this function is NOT thread safe
7967 QgsVectorLayer *targetLayer = QgsExpressionUtils::getVectorLayer( targetLayerValue, context, parent );
7969 if ( !targetLayer ) // No layer, no joy
7970 {
7971 parent->setEvalErrorString( QObject::tr( "Layer '%1' could not be loaded." ).arg( targetLayerValue.toString() ) );
7972 return QVariant();
7973 }
7974
7975 // Third parameter is the filtering expression
7976 node = QgsExpressionUtils::getNode( values.at( 2 ), parent );
7978 QString filterString = node->dump();
7979 if ( filterString != "NULL" )
7980 {
7981 request.setFilterExpression( filterString ); //filter cached features
7982 }
7983
7984 // Fourth parameter is the limit
7985 node = QgsExpressionUtils::getNode( values.at( 3 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
7987 QVariant limitValue = node->eval( parent, context );
7989 qlonglong limit = QgsExpressionUtils::getIntValue( limitValue, parent );
7990
7991 // Fifth parameter (for nearest only) is the max distance
7992 double max_distance = 0;
7993 if ( isNearestFunc ) //maxdistance param handling
7994 {
7995 node = QgsExpressionUtils::getNode( values.at( 4 ), parent );
7997 QVariant distanceValue = node->eval( parent, context );
7999 max_distance = QgsExpressionUtils::getDoubleValue( distanceValue, parent );
8000 }
8001
8002 // Fifth or sixth (for nearest only) parameter is the cache toggle
8003 node = QgsExpressionUtils::getNode( values.at( isNearestFunc ? 5 : 4 ), parent );
8005 QVariant cacheValue = node->eval( parent, context );
8007 bool cacheEnabled = cacheValue.toBool();
8008
8009 // Sixth parameter (for intersects only) is the min overlap (area or length)
8010 // Seventh parameter (for intersects only) is the min inscribed circle radius
8011 // Eighth parameter (for intersects only) is the return_details
8012 // Ninth parameter (for intersects only) is the sort_by_intersection_size flag
8013 double minOverlap { -1 };
8014 double minInscribedCircleRadius { -1 };
8015 bool returnDetails = false; //#spellok
8016 bool sortByMeasure = false;
8017 bool sortAscending = false;
8018 bool requireMeasures = false;
8019 bool overlapOrRadiusFilter = false;
8020 if ( isIntersectsFunc )
8021 {
8022
8023 node = QgsExpressionUtils::getNode( values.at( 5 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8025 const QVariant minOverlapValue = node->eval( parent, context );
8027 minOverlap = QgsExpressionUtils::getDoubleValue( minOverlapValue, parent );
8028 node = QgsExpressionUtils::getNode( values.at( 6 ), parent ); //in expressions overlay functions throw the exception: Eval Error: Cannot convert '' to int
8030 const QVariant minInscribedCircleRadiusValue = node->eval( parent, context );
8032 minInscribedCircleRadius = QgsExpressionUtils::getDoubleValue( minInscribedCircleRadiusValue, parent );
8033 node = QgsExpressionUtils::getNode( values.at( 7 ), parent );
8034 // Return measures is only effective when an expression is set
8035 returnDetails = !testOnly && node->eval( parent, context ).toBool(); //#spellok
8036 node = QgsExpressionUtils::getNode( values.at( 8 ), parent );
8037 // Sort by measures is only effective when an expression is set
8038 const QString sorting { node->eval( parent, context ).toString().toLower() };
8039 sortByMeasure = !testOnly && ( sorting.startsWith( "asc" ) || sorting.startsWith( "des" ) );
8040 sortAscending = sorting.startsWith( "asc" );
8041 requireMeasures = sortByMeasure || returnDetails; //#spellok
8042 overlapOrRadiusFilter = minInscribedCircleRadius != -1 || minOverlap != -1;
8043 }
8044
8045
8046 FEAT_FROM_CONTEXT( context, feat )
8047 const QgsGeometry geometry = feat.geometry();
8048
8049 if ( sourceLayer && targetLayer->crs() != sourceLayer->crs() )
8050 {
8051 QgsCoordinateTransformContext TransformContext = context->variable( QStringLiteral( "_project_transform_context" ) ).value<QgsCoordinateTransformContext>();
8052 request.setDestinationCrs( sourceLayer->crs(), TransformContext ); //if crs are not the same, cached target will be reprojected to source crs
8053 }
8054
8055 bool sameLayers = ( sourceLayer && sourceLayer->id() == targetLayer->id() );
8056
8057 QgsRectangle intDomain = geometry.boundingBox();
8058 if ( bboxGrow != 0 )
8059 {
8060 intDomain.grow( bboxGrow ); //optional parameter to enlarge boundary context for touches and equals methods
8061 }
8062
8063 const QString cacheBase { QStringLiteral( "%1:%2:%3" ).arg( targetLayer->id(), subExpString, filterString ) };
8064
8065 // Cache (a local spatial index) is always enabled for nearest function (as we need QgsSpatialIndex::nearestNeighbor)
8066 // Otherwise, it can be toggled by the user
8067 QgsSpatialIndex spatialIndex;
8068 QgsVectorLayer *cachedTarget;
8069 QList<QgsFeature> features;
8070 if ( isNearestFunc || ( layerCanBeCached && cacheEnabled ) )
8071 {
8072 // If the cache (local spatial index) is enabled, we materialize the whole
8073 // layer, then do the request on that layer instead.
8074 const QString cacheLayer { QStringLiteral( "ovrlaylyr:%1" ).arg( cacheBase ) };
8075 const QString cacheIndex { QStringLiteral( "ovrlayidx:%1" ).arg( cacheBase ) };
8076
8077 if ( !context->hasCachedValue( cacheLayer ) ) // should check for same crs. if not the same we could think to reproject target layer before charging cache
8078 {
8079 cachedTarget = targetLayer->materialize( request );
8080 if ( layerCanBeCached )
8081 context->setCachedValue( cacheLayer, QVariant::fromValue( cachedTarget ) );
8082 }
8083 else
8084 {
8085 cachedTarget = context->cachedValue( cacheLayer ).value<QgsVectorLayer *>();
8086 }
8087
8088 if ( !context->hasCachedValue( cacheIndex ) )
8089 {
8090 spatialIndex = QgsSpatialIndex( cachedTarget->getFeatures(), nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries );
8091 if ( layerCanBeCached )
8092 context->setCachedValue( cacheIndex, QVariant::fromValue( spatialIndex ) );
8093 }
8094 else
8095 {
8096 spatialIndex = context->cachedValue( cacheIndex ).value<QgsSpatialIndex>();
8097 }
8098
8099 QList<QgsFeatureId> fidsList;
8100 if ( isNearestFunc )
8101 {
8102 fidsList = spatialIndex.nearestNeighbor( geometry, sameLayers ? limit + 1 : limit, max_distance );
8103 }
8104 else
8105 {
8106 fidsList = spatialIndex.intersects( intDomain );
8107 }
8108
8109 QListIterator<QgsFeatureId> i( fidsList );
8110 while ( i.hasNext() )
8111 {
8112 QgsFeatureId fId2 = i.next();
8113 if ( sameLayers && feat.id() == fId2 )
8114 continue;
8115 features.append( cachedTarget->getFeature( fId2 ) );
8116 }
8117
8118 }
8119 else
8120 {
8121 // If the cache (local spatial index) is not enabled, we directly
8122 // get the features from the target layer
8123 request.setFilterRect( intDomain );
8124 QgsFeatureIterator fit = targetLayer->getFeatures( request );
8125 QgsFeature feat2;
8126 while ( fit.nextFeature( feat2 ) )
8127 {
8128 if ( sameLayers && feat.id() == feat2.id() )
8129 continue;
8130 features.append( feat2 );
8131 }
8132 }
8133
8134 QgsExpression subExpression;
8135 QgsExpressionContext subContext;
8136 if ( !testOnly )
8137 {
8138 const QString expCacheKey { QStringLiteral( "exp:%1" ).arg( cacheBase ) };
8139 const QString ctxCacheKey { QStringLiteral( "ctx:%1" ).arg( cacheBase ) };
8140
8141 if ( !context->hasCachedValue( expCacheKey ) || !context->hasCachedValue( ctxCacheKey ) )
8142 {
8143 subExpression = QgsExpression( subExpString );
8145 subExpression.prepare( &subContext );
8146 }
8147 else
8148 {
8149 subExpression = context->cachedValue( expCacheKey ).value<QgsExpression>();
8150 subContext = context->cachedValue( ctxCacheKey ).value<QgsExpressionContext>();
8151 }
8152 }
8153
8154 // //////////////////////////////////////////////////////////////////
8155 // Helper functions for geometry tests
8156
8157 // Test function for linestring geometries, returns TRUE if test passes
8158 auto testLinestring = [minOverlap, requireMeasures]( const QgsGeometry intersection, double & overlapValue ) -> bool
8159 {
8160 bool testResult { false };
8161 // For return measures:
8162 QVector<double> overlapValues;
8163 const QgsGeometry merged { intersection.mergeLines() };
8164 for ( auto it = merged.const_parts_begin(); ! testResult && it != merged.const_parts_end(); ++it )
8165 {
8167 // Check min overlap for intersection (if set)
8168 if ( minOverlap != -1 || requireMeasures )
8169 {
8170 overlapValue = geom->length();
8171 overlapValues.append( overlapValue );
8172 if ( minOverlap != -1 )
8173 {
8174 if ( overlapValue >= minOverlap )
8175 {
8176 testResult = true;
8177 }
8178 else
8179 {
8180 continue;
8181 }
8182 }
8183 }
8184 }
8185
8186 if ( ! overlapValues.isEmpty() )
8187 {
8188 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8189 }
8190
8191 return testResult;
8192 };
8193
8194 // Test function for polygon geometries, returns TRUE if test passes
8195 auto testPolygon = [minOverlap, requireMeasures, minInscribedCircleRadius]( const QgsGeometry intersection, double & radiusValue, double & overlapValue ) -> bool
8196 {
8197 // overlap and inscribed circle tests must be checked both (if the values are != -1)
8198 bool testResult { false };
8199 // For return measures:
8200 QVector<double> overlapValues;
8201 QVector<double> radiusValues;
8202 for ( auto it = intersection.const_parts_begin(); ( ! testResult || requireMeasures ) && it != intersection.const_parts_end(); ++it )
8203 {
8205 // Check min overlap for intersection (if set)
8206 if ( minOverlap != -1 || requireMeasures )
8207 {
8208 overlapValue = geom->area();
8209 overlapValues.append( geom->area() );
8210 if ( minOverlap != - 1 )
8211 {
8212 if ( overlapValue >= minOverlap )
8213 {
8214 testResult = true;
8215 }
8216 else
8217 {
8218 continue;
8219 }
8220 }
8221 }
8222
8223 // Check min inscribed circle radius for intersection (if set)
8224 if ( minInscribedCircleRadius != -1 || requireMeasures )
8225 {
8226 const QgsRectangle bbox = geom->boundingBox();
8227 const double width = bbox.width();
8228 const double height = bbox.height();
8229 const double size = width > height ? width : height;
8230 const double tolerance = size / 100.0;
8231 radiusValue = QgsGeos( geom ).maximumInscribedCircle( tolerance )->length();
8232 testResult = radiusValue >= minInscribedCircleRadius;
8233 radiusValues.append( radiusValues );
8234 }
8235 } // end for parts
8236
8237 // Get the max values
8238 if ( !radiusValues.isEmpty() )
8239 {
8240 radiusValue = *std::max_element( radiusValues.cbegin(), radiusValues.cend() );
8241 }
8242
8243 if ( ! overlapValues.isEmpty() )
8244 {
8245 overlapValue = *std::max_element( overlapValues.cbegin(), overlapValues.cend() );
8246 }
8247
8248 return testResult;
8249
8250 };
8251
8252
8253 bool found = false;
8254 int foundCount = 0;
8255 QVariantList results;
8256
8257 QListIterator<QgsFeature> i( features );
8258 while ( i.hasNext() && ( sortByMeasure || limit == -1 || foundCount < limit ) )
8259 {
8260
8261 QgsFeature feat2 = i.next();
8262
8263
8264 if ( ! relationFunction || ( geometry.*relationFunction )( feat2.geometry() ) ) // Calls the method provided as template argument for the function (e.g. QgsGeometry::intersects)
8265 {
8266
8267 double overlapValue = -1;
8268 double radiusValue = -1;
8269
8270 if ( isIntersectsFunc && ( requireMeasures || overlapOrRadiusFilter ) )
8271 {
8272
8273 QgsGeometry intersection { geometry.intersection( feat2.geometry() ) };
8274
8275 // Pre-process collections: if the tested geometry is a polygon we take the polygons from the collection
8276 if ( intersection.wkbType() == Qgis::WkbType::GeometryCollection )
8277 {
8278 const QVector<QgsGeometry> geometries { intersection.asGeometryCollection() };
8279 intersection = QgsGeometry();
8280 QgsMultiPolygonXY poly;
8281 QgsMultiPolylineXY line;
8282 QgsMultiPointXY point;
8283 for ( const auto &geom : std::as_const( geometries ) )
8284 {
8285 switch ( geom.type() )
8286 {
8288 {
8289 poly.append( geom.asPolygon() );
8290 break;
8291 }
8293 {
8294 line.append( geom.asPolyline() );
8295 break;
8296 }
8298 {
8299 point.append( geom.asPoint() );
8300 break;
8301 }
8304 {
8305 break;
8306 }
8307 }
8308 }
8309
8310 switch ( geometry.type() )
8311 {
8313 {
8314 intersection = QgsGeometry::fromMultiPolygonXY( poly );
8315 break;
8316 }
8318 {
8319 intersection = QgsGeometry::fromMultiPolylineXY( line );
8320 break;
8321 }
8323 {
8324 intersection = QgsGeometry::fromMultiPointXY( point );
8325 break;
8326 }
8329 {
8330 break;
8331 }
8332 }
8333 }
8334
8335 // Depending on the intersection geometry type and on the geometry type of
8336 // the tested geometry we can run different tests and collect different measures
8337 // that can be used for sorting (if required).
8338 switch ( intersection.type() )
8339 {
8340
8342 {
8343
8344 // Overlap and inscribed circle tests must be checked both (if the values are != -1)
8345 bool testResult { testPolygon( intersection, radiusValue, overlapValue ) };
8346
8347 if ( ! testResult && overlapOrRadiusFilter )
8348 {
8349 continue;
8350 }
8351
8352 break;
8353 }
8354
8356 {
8357
8358 // If the intersection is a linestring and a minimum circle is required
8359 // we can discard this result immediately.
8360 if ( minInscribedCircleRadius != -1 )
8361 {
8362 continue;
8363 }
8364
8365 // Otherwise a test for the overlap value is performed.
8366 const bool testResult { testLinestring( intersection, overlapValue ) };
8367
8368 if ( ! testResult && overlapOrRadiusFilter )
8369 {
8370 continue;
8371 }
8372
8373 break;
8374 }
8375
8377 {
8378
8379 // If the intersection is a point and a minimum circle is required
8380 // we can discard this result immediately.
8381 if ( minInscribedCircleRadius != -1 )
8382 {
8383 continue;
8384 }
8385
8386 bool testResult { false };
8387 if ( minOverlap != -1 || requireMeasures )
8388 {
8389 // Initially set this to 0 because it's a point intersection...
8390 overlapValue = 0;
8391 // ... but if the target geometry is not a point and the source
8392 // geometry is a point, we must record the length or the area
8393 // of the intersected geometry and use that as a measure for
8394 // sorting or reporting.
8395 if ( geometry.type() == Qgis::GeometryType::Point )
8396 {
8397 switch ( feat2.geometry().type() )
8398 {
8402 {
8403 break;
8404 }
8406 {
8407 testResult = testLinestring( feat2.geometry(), overlapValue );
8408 break;
8409 }
8411 {
8412 testResult = testPolygon( feat2.geometry(), radiusValue, overlapValue );
8413 break;
8414 }
8415 }
8416 }
8417
8418 if ( ! testResult && overlapOrRadiusFilter )
8419 {
8420 continue;
8421 }
8422
8423 }
8424 break;
8425 }
8426
8429 {
8430 continue;
8431 }
8432 }
8433 }
8434
8435 found = true;
8436 foundCount++;
8437
8438 // We just want a single boolean result if there is any intersect: finish and return true
8439 if ( testOnly )
8440 break;
8441
8442 if ( !invert )
8443 {
8444 // We want a list of attributes / geometries / other expression values, evaluate now
8445 subContext.setFeature( feat2 );
8446 const QVariant expResult = subExpression.evaluate( &subContext );
8447
8448 if ( requireMeasures )
8449 {
8450 QVariantMap resultRecord;
8451 resultRecord.insert( QStringLiteral( "id" ), feat2.id() );
8452 resultRecord.insert( QStringLiteral( "result" ), expResult );
8453 // Overlap is always added because return measures was set
8454 resultRecord.insert( QStringLiteral( "overlap" ), overlapValue );
8455 // Radius is only added when is different than -1 (because for linestrings is not set)
8456 if ( radiusValue != -1 )
8457 {
8458 resultRecord.insert( QStringLiteral( "radius" ), radiusValue );
8459 }
8460 results.append( resultRecord );
8461 }
8462 else
8463 {
8464 results.append( expResult );
8465 }
8466 }
8467 else
8468 {
8469 // If not, results is a list of found ids, which we'll inverse and evaluate below
8470 results.append( feat2.id() );
8471 }
8472 }
8473 }
8474
8475 if ( testOnly )
8476 {
8477 if ( invert )
8478 found = !found;//for disjoint condition
8479 return found;
8480 }
8481
8482 if ( !invert )
8483 {
8484 if ( requireMeasures )
8485 {
8486 if ( sortByMeasure )
8487 {
8488 std::sort( results.begin(), results.end(), [ sortAscending ]( const QVariant & recordA, const QVariant & recordB ) -> bool
8489 {
8490 return sortAscending ?
8491 recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble()
8492 : recordA.toMap().value( QStringLiteral( "overlap" ) ).toDouble() > recordB.toMap().value( QStringLiteral( "overlap" ) ).toDouble();
8493 } );
8494 }
8495 // Resize
8496 if ( limit > 0 && results.size() > limit )
8497 {
8498 results.erase( results.begin() + limit );
8499 }
8500
8501 if ( ! returnDetails ) //#spellok
8502 {
8503 QVariantList expResults;
8504 for ( auto it = results.constBegin(); it != results.constEnd(); ++it )
8505 {
8506 expResults.append( it->toMap().value( QStringLiteral( "result" ) ) );
8507 }
8508 return expResults;
8509 }
8510 }
8511
8512 return results;
8513 }
8514
8515 // for disjoint condition returns the results for cached layers not intersected feats
8516 QVariantList disjoint_results;
8517 QgsFeature feat2;
8518 QgsFeatureRequest request2;
8519 request2.setLimit( limit );
8520 if ( context )
8521 request2.setFeedback( context->feedback() );
8522 QgsFeatureIterator fi = targetLayer->getFeatures( request2 );
8523 while ( fi.nextFeature( feat2 ) )
8524 {
8525 if ( !results.contains( feat2.id() ) )
8526 {
8527 subContext.setFeature( feat2 );
8528 disjoint_results.append( subExpression.evaluate( &subContext ) );
8529 }
8530 }
8531 return disjoint_results;
8532
8533}
8534
8535// Intersect functions:
8536
8537static QVariant fcnGeomOverlayIntersects( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8538{
8539 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, false, 0, false, true );
8540}
8541
8542static QVariant fcnGeomOverlayContains( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8543{
8544 return executeGeomOverlay( values, context, parent, &QgsGeometry::contains );
8545}
8546
8547static QVariant fcnGeomOverlayCrosses( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8548{
8549 return executeGeomOverlay( values, context, parent, &QgsGeometry::crosses );
8550}
8551
8552static QVariant fcnGeomOverlayEquals( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8553{
8554 return executeGeomOverlay( values, context, parent, &QgsGeometry::equals, false, 0.01 ); //grow amount should adapt to current units
8555}
8556
8557static QVariant fcnGeomOverlayTouches( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8558{
8559 return executeGeomOverlay( values, context, parent, &QgsGeometry::touches, false, 0.01 ); //grow amount should adapt to current units
8560}
8561
8562static QVariant fcnGeomOverlayWithin( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8563{
8564 return executeGeomOverlay( values, context, parent, &QgsGeometry::within );
8565}
8567static QVariant fcnGeomOverlayDisjoint( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8568{
8569 return executeGeomOverlay( values, context, parent, &QgsGeometry::intersects, true, 0, false, true );
8570}
8571
8572static QVariant fcnGeomOverlayNearest( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
8573{
8574 return executeGeomOverlay( values, context, parent, nullptr, false, 0, true );
8575}
8576
8577const QList<QgsExpressionFunction *> &QgsExpression::Functions()
8578{
8579 // The construction of the list isn't thread-safe, and without the mutex,
8580 // crashes in the WFS provider may occur, since it can parse expressions
8581 // in parallel.
8582 // The mutex needs to be recursive.
8583 QMutexLocker locker( &sFunctionsMutex );
8584
8585 QList<QgsExpressionFunction *> &functions = *sFunctions();
8586
8587 if ( functions.isEmpty() )
8588 {
8590 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
8591 << QgsExpressionFunction::Parameter( QStringLiteral( "group_by" ), true )
8592 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true );
8593
8594 QgsExpressionFunction::ParameterList aggParamsConcat = aggParams;
8595 aggParamsConcat << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8596 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8597
8598 QgsExpressionFunction::ParameterList aggParamsArray = aggParams;
8599 aggParamsArray << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true );
8600
8601 functions
8602 << new QgsStaticExpressionFunction( QStringLiteral( "sqrt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnSqrt, QStringLiteral( "Math" ) )
8603 << new QgsStaticExpressionFunction( QStringLiteral( "radians" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "degrees" ) ), fcnRadians, QStringLiteral( "Math" ) )
8604 << new QgsStaticExpressionFunction( QStringLiteral( "degrees" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "radians" ) ), fcnDegrees, QStringLiteral( "Math" ) )
8605 << new QgsStaticExpressionFunction( QStringLiteral( "azimuth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnAzimuth, QStringLiteral( "GeometryGroup" ) )
8606 << new QgsStaticExpressionFunction( QStringLiteral( "bearing" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "source_crs" ), true, QVariant() ) << QgsExpressionFunction::Parameter( QStringLiteral( "ellipsoid" ), true, QVariant() ), fcnBearing, QStringLiteral( "GeometryGroup" ) )
8607 << new QgsStaticExpressionFunction( QStringLiteral( "inclination" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point_b" ) ), fcnInclination, QStringLiteral( "GeometryGroup" ) )
8608 << 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" ) )
8609 << new QgsStaticExpressionFunction( QStringLiteral( "abs" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAbs, QStringLiteral( "Math" ) )
8610 << new QgsStaticExpressionFunction( QStringLiteral( "cos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnCos, QStringLiteral( "Math" ) )
8611 << new QgsStaticExpressionFunction( QStringLiteral( "sin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnSin, QStringLiteral( "Math" ) )
8612 << new QgsStaticExpressionFunction( QStringLiteral( "tan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "angle" ) ), fcnTan, QStringLiteral( "Math" ) )
8613 << new QgsStaticExpressionFunction( QStringLiteral( "asin" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAsin, QStringLiteral( "Math" ) )
8614 << new QgsStaticExpressionFunction( QStringLiteral( "acos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAcos, QStringLiteral( "Math" ) )
8615 << new QgsStaticExpressionFunction( QStringLiteral( "atan" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnAtan, QStringLiteral( "Math" ) )
8616 << new QgsStaticExpressionFunction( QStringLiteral( "atan2" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ), fcnAtan2, QStringLiteral( "Math" ) )
8617 << new QgsStaticExpressionFunction( QStringLiteral( "exp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnExp, QStringLiteral( "Math" ) )
8618 << new QgsStaticExpressionFunction( QStringLiteral( "ln" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLn, QStringLiteral( "Math" ) )
8619 << new QgsStaticExpressionFunction( QStringLiteral( "log10" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog10, QStringLiteral( "Math" ) )
8620 << new QgsStaticExpressionFunction( QStringLiteral( "log" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "base" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLog, QStringLiteral( "Math" ) )
8621 << new QgsStaticExpressionFunction( QStringLiteral( "round" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 ), fcnRound, QStringLiteral( "Math" ) );
8622
8623 QgsStaticExpressionFunction *randFunc = new QgsStaticExpressionFunction( QStringLiteral( "rand" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true ), fcnRnd, QStringLiteral( "Math" ) );
8624 randFunc->setIsStatic( false );
8625 functions << randFunc;
8626
8627 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" ) );
8628 randfFunc->setIsStatic( false );
8629 functions << randfFunc;
8630
8631 functions
8632 << new QgsStaticExpressionFunction( QStringLiteral( "max" ), -1, fcnMax, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8633 << new QgsStaticExpressionFunction( QStringLiteral( "min" ), -1, fcnMin, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList(), /* handlesNull = */ true )
8634 << new QgsStaticExpressionFunction( QStringLiteral( "clamp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "min" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "max" ) ), fcnClamp, QStringLiteral( "Math" ) )
8635 << 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" ) )
8636 << new QgsStaticExpressionFunction( QStringLiteral( "scale_polynomial" ), 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" ) ), fcnPolynomialScale, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "scale_exp" ) )
8637 << new QgsStaticExpressionFunction( QStringLiteral( "scale_exponential" ), 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" ) ), fcnExponentialScale, QStringLiteral( "Math" ) )
8638 << new QgsStaticExpressionFunction( QStringLiteral( "floor" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnFloor, QStringLiteral( "Math" ) )
8639 << new QgsStaticExpressionFunction( QStringLiteral( "ceil" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnCeil, QStringLiteral( "Math" ) )
8640 << new QgsStaticExpressionFunction( QStringLiteral( "pi" ), 0, fcnPi, QStringLiteral( "Math" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$pi" ) )
8641 << new QgsStaticExpressionFunction( QStringLiteral( "to_bool" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToBool, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "tobool" ), /* handlesNull = */ true )
8642 << new QgsStaticExpressionFunction( QStringLiteral( "to_int" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToInt, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toint" ) )
8643 << new QgsStaticExpressionFunction( QStringLiteral( "to_real" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToReal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "toreal" ) )
8644 << 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" ) )
8645 << 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" ) )
8646 << 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" ) )
8647 << 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" ) )
8648 << 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" ) )
8649 << 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" ) )
8650 << 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" ) )
8651 << new QgsStaticExpressionFunction( QStringLiteral( "to_decimal" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnToDecimal, QStringLiteral( "Conversions" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "todecimal" ) )
8652 << new QgsStaticExpressionFunction( QStringLiteral( "extract_degrees" ), { QgsExpressionFunction::Parameter{ QStringLiteral( "value" ) } }, fcnExtractDegrees, QStringLiteral( "Conversions" ) )
8653 << new QgsStaticExpressionFunction( QStringLiteral( "extract_minutes" ), { QgsExpressionFunction::Parameter{ QStringLiteral( "value" ) } }, fcnExtractMinutes, QStringLiteral( "Conversions" ) )
8654 << new QgsStaticExpressionFunction( QStringLiteral( "extract_seconds" ), { QgsExpressionFunction::Parameter{ QStringLiteral( "value" ) } }, fcnExtractSeconds, QStringLiteral( "Conversions" ) )
8655 << new QgsStaticExpressionFunction( QStringLiteral( "coalesce" ), -1, fcnCoalesce, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8656 << new QgsStaticExpressionFunction( QStringLiteral( "nullif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value2" ) ), fcnNullIf, QStringLiteral( "Conditionals" ) )
8657 << 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 )
8658 << new QgsStaticExpressionFunction( QStringLiteral( "try" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "alternative" ), true, QVariant() ), fcnTry, QStringLiteral( "Conditionals" ), QString(), false, QSet<QString>(), true )
8659
8660 << new QgsStaticExpressionFunction( QStringLiteral( "aggregate" ),
8662 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
8663 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8664 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8665 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
8666 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8667 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8668 fcnAggregate,
8669 QStringLiteral( "Aggregates" ),
8670 QString(),
8671 []( const QgsExpressionNodeFunction * node )
8672 {
8673 // usesGeometry callback: return true if @parent variable is referenced
8674
8675 if ( !node )
8676 return true;
8677
8678 if ( !node->args() )
8679 return false;
8680
8681 QSet<QString> referencedVars;
8682 if ( node->args()->count() > 2 )
8683 {
8684 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8685 referencedVars = subExpressionNode->referencedVariables();
8686 }
8687
8688 if ( node->args()->count() > 3 )
8689 {
8690 QgsExpressionNode *filterNode = node->args()->at( 3 );
8691 referencedVars.unite( filterNode->referencedVariables() );
8692 }
8693 return referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() );
8694 },
8695 []( const QgsExpressionNodeFunction * node )
8696 {
8697 // referencedColumns callback: return AllAttributes if @parent variable is referenced
8698
8699 if ( !node )
8700 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8701
8702 if ( !node->args() )
8703 return QSet<QString>();
8704
8705 QSet<QString> referencedCols;
8706 QSet<QString> referencedVars;
8707
8708 if ( node->args()->count() > 2 )
8709 {
8710 QgsExpressionNode *subExpressionNode = node->args()->at( 2 );
8711 referencedVars = subExpressionNode->referencedVariables();
8712 referencedCols = subExpressionNode->referencedColumns();
8713 }
8714 if ( node->args()->count() > 3 )
8715 {
8716 QgsExpressionNode *filterNode = node->args()->at( 3 );
8717 referencedVars = filterNode->referencedVariables();
8718 referencedCols.unite( filterNode->referencedColumns() );
8719 }
8720
8721 if ( referencedVars.contains( QStringLiteral( "parent" ) ) || referencedVars.contains( QString() ) )
8722 return QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES;
8723 else
8724 return referencedCols;
8725 },
8726 true
8727 )
8728
8729 << new QgsStaticExpressionFunction( QStringLiteral( "relation_aggregate" ), QgsExpressionFunction::ParameterList()
8730 << QgsExpressionFunction::Parameter( QStringLiteral( "relation" ) )
8731 << QgsExpressionFunction::Parameter( QStringLiteral( "aggregate" ) )
8732 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), false, QVariant(), true )
8733 << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true )
8734 << QgsExpressionFunction::Parameter( QStringLiteral( "order_by" ), true, QVariant(), true ),
8735 fcnAggregateRelation, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true )
8736
8737 << new QgsStaticExpressionFunction( QStringLiteral( "count" ), aggParams, fcnAggregateCount, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8738 << new QgsStaticExpressionFunction( QStringLiteral( "count_distinct" ), aggParams, fcnAggregateCountDistinct, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8739 << new QgsStaticExpressionFunction( QStringLiteral( "count_missing" ), aggParams, fcnAggregateCountMissing, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8740 << new QgsStaticExpressionFunction( QStringLiteral( "minimum" ), aggParams, fcnAggregateMin, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8741 << new QgsStaticExpressionFunction( QStringLiteral( "maximum" ), aggParams, fcnAggregateMax, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8742 << new QgsStaticExpressionFunction( QStringLiteral( "sum" ), aggParams, fcnAggregateSum, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8743 << new QgsStaticExpressionFunction( QStringLiteral( "mean" ), aggParams, fcnAggregateMean, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8744 << new QgsStaticExpressionFunction( QStringLiteral( "median" ), aggParams, fcnAggregateMedian, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8745 << new QgsStaticExpressionFunction( QStringLiteral( "stdev" ), aggParams, fcnAggregateStdev, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8746 << new QgsStaticExpressionFunction( QStringLiteral( "range" ), aggParams, fcnAggregateRange, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8747 << new QgsStaticExpressionFunction( QStringLiteral( "minority" ), aggParams, fcnAggregateMinority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8748 << new QgsStaticExpressionFunction( QStringLiteral( "majority" ), aggParams, fcnAggregateMajority, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8749 << new QgsStaticExpressionFunction( QStringLiteral( "q1" ), aggParams, fcnAggregateQ1, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8750 << new QgsStaticExpressionFunction( QStringLiteral( "q3" ), aggParams, fcnAggregateQ3, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8751 << new QgsStaticExpressionFunction( QStringLiteral( "iqr" ), aggParams, fcnAggregateIQR, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8752 << new QgsStaticExpressionFunction( QStringLiteral( "min_length" ), aggParams, fcnAggregateMinLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8753 << new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8754 << new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8755 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParamsConcat, fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8756 << new QgsStaticExpressionFunction( QStringLiteral( "concatenate_unique" ), aggParamsConcat, fcnAggregateStringConcatUnique, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8757 << new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParamsArray, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
8758
8759 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
8760 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
8761
8762 << new QgsStaticExpressionFunction( QStringLiteral( "now" ), 0, fcnNow, QStringLiteral( "Date and Time" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "$now" ) )
8763 << new QgsStaticExpressionFunction( QStringLiteral( "age" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime1" ) )
8764 << QgsExpressionFunction::Parameter( QStringLiteral( "datetime2" ) ),
8765 fcnAge, QStringLiteral( "Date and Time" ) )
8766 << new QgsStaticExpressionFunction( QStringLiteral( "year" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnYear, QStringLiteral( "Date and Time" ) )
8767 << new QgsStaticExpressionFunction( QStringLiteral( "month" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnMonth, QStringLiteral( "Date and Time" ) )
8768 << new QgsStaticExpressionFunction( QStringLiteral( "week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnWeek, QStringLiteral( "Date and Time" ) )
8769 << new QgsStaticExpressionFunction( QStringLiteral( "day" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDay, QStringLiteral( "Date and Time" ) )
8770 << new QgsStaticExpressionFunction( QStringLiteral( "hour" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnHour, QStringLiteral( "Date and Time" ) )
8771 << new QgsStaticExpressionFunction( QStringLiteral( "minute" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnMinute, QStringLiteral( "Date and Time" ) )
8772 << new QgsStaticExpressionFunction( QStringLiteral( "second" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), fcnSeconds, QStringLiteral( "Date and Time" ) )
8773 << new QgsStaticExpressionFunction( QStringLiteral( "epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnEpoch, QStringLiteral( "Date and Time" ) )
8774 << new QgsStaticExpressionFunction( QStringLiteral( "datetime_from_epoch" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "long" ) ), fcnDateTimeFromEpoch, QStringLiteral( "Date and Time" ) )
8775 << new QgsStaticExpressionFunction( QStringLiteral( "day_of_week" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "date" ) ), fcnDayOfWeek, QStringLiteral( "Date and Time" ) )
8776 << new QgsStaticExpressionFunction( QStringLiteral( "make_date" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8777 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8778 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) ),
8779 fcnMakeDate, QStringLiteral( "Date and Time" ) )
8780 << new QgsStaticExpressionFunction( QStringLiteral( "make_time" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8781 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8782 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8783 fcnMakeTime, QStringLiteral( "Date and Time" ) )
8784 << new QgsStaticExpressionFunction( QStringLiteral( "make_datetime" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "year" ) )
8785 << QgsExpressionFunction::Parameter( QStringLiteral( "month" ) )
8786 << QgsExpressionFunction::Parameter( QStringLiteral( "day" ) )
8787 << QgsExpressionFunction::Parameter( QStringLiteral( "hour" ) )
8788 << QgsExpressionFunction::Parameter( QStringLiteral( "minute" ) )
8789 << QgsExpressionFunction::Parameter( QStringLiteral( "second" ) ),
8790 fcnMakeDateTime, QStringLiteral( "Date and Time" ) )
8791 << new QgsStaticExpressionFunction( QStringLiteral( "make_interval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "years" ), true, 0 )
8792 << QgsExpressionFunction::Parameter( QStringLiteral( "months" ), true, 0 )
8793 << QgsExpressionFunction::Parameter( QStringLiteral( "weeks" ), true, 0 )
8794 << QgsExpressionFunction::Parameter( QStringLiteral( "days" ), true, 0 )
8795 << QgsExpressionFunction::Parameter( QStringLiteral( "hours" ), true, 0 )
8796 << QgsExpressionFunction::Parameter( QStringLiteral( "minutes" ), true, 0 )
8797 << QgsExpressionFunction::Parameter( QStringLiteral( "seconds" ), true, 0 ),
8798 fcnMakeInterval, QStringLiteral( "Date and Time" ) )
8799 << new QgsStaticExpressionFunction( QStringLiteral( "timezone_from_id" ), { QgsExpressionFunction::Parameter( QStringLiteral( "id" ) ) }, fcnTimeZoneFromId, QStringLiteral( "Date and Time" ) )
8800 << new QgsStaticExpressionFunction( QStringLiteral( "timezone_id" ), { QgsExpressionFunction::Parameter( QStringLiteral( "timezone" ) ) }, fcnTimeZoneToId, QStringLiteral( "Date and Time" ) )
8801 << new QgsStaticExpressionFunction( QStringLiteral( "get_timezone" ), { QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ) }, fcnGetTimeZone, QStringLiteral( "Date and Time" ) )
8802 << new QgsStaticExpressionFunction( QStringLiteral( "set_timezone" ), { QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), QgsExpressionFunction::Parameter( QStringLiteral( "timezone" ) ) }, fcnSetTimeZone, QStringLiteral( "Date and Time" ) )
8803 << new QgsStaticExpressionFunction( QStringLiteral( "convert_timezone" ), { QgsExpressionFunction::Parameter( QStringLiteral( "datetime" ) ), QgsExpressionFunction::Parameter( QStringLiteral( "timezone" ) ) }, fcnConvertTimeZone, QStringLiteral( "Date and Time" ) )
8804 << new QgsStaticExpressionFunction( QStringLiteral( "lower" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnLower, QStringLiteral( "String" ) )
8805 << new QgsStaticExpressionFunction( QStringLiteral( "substr_count" ), QgsExpressionFunction::ParameterList()
8806 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8807 << QgsExpressionFunction::Parameter( QStringLiteral( "substring" ) )
8808 << QgsExpressionFunction::Parameter( QStringLiteral( "overlapping" ), true, false ), // Optional parameter with default value of false
8809 fcnSubstrCount,
8810 QStringLiteral( "String" ) )
8811 << new QgsStaticExpressionFunction( QStringLiteral( "upper" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnUpper, QStringLiteral( "String" ) )
8812 << new QgsStaticExpressionFunction( QStringLiteral( "title" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTitle, QStringLiteral( "String" ) )
8813 << new QgsStaticExpressionFunction( QStringLiteral( "trim" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnTrim, QStringLiteral( "String" ) )
8814 << new QgsStaticExpressionFunction( QStringLiteral( "ltrim" ), QgsExpressionFunction::ParameterList()
8815 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8816 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnLTrim, QStringLiteral( "String" ) )
8817 << new QgsStaticExpressionFunction( QStringLiteral( "rtrim" ), QgsExpressionFunction::ParameterList()
8818 << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) )
8819 << QgsExpressionFunction::Parameter( QStringLiteral( "characters" ), true, QStringLiteral( " " ) ), fcnRTrim, QStringLiteral( "String" ) )
8820 << new QgsStaticExpressionFunction( QStringLiteral( "levenshtein" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLevenshtein, QStringLiteral( "Fuzzy Matching" ) )
8821 << new QgsStaticExpressionFunction( QStringLiteral( "longest_common_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnLCS, QStringLiteral( "Fuzzy Matching" ) )
8822 << new QgsStaticExpressionFunction( QStringLiteral( "hamming_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "string2" ) ), fcnHamming, QStringLiteral( "Fuzzy Matching" ) )
8823 << new QgsStaticExpressionFunction( QStringLiteral( "soundex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnSoundex, QStringLiteral( "Fuzzy Matching" ) )
8824 << new QgsStaticExpressionFunction( QStringLiteral( "char" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "code" ) ), fcnChar, QStringLiteral( "String" ) )
8825 << new QgsStaticExpressionFunction( QStringLiteral( "ascii" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnAscii, QStringLiteral( "String" ) )
8826 << new QgsStaticExpressionFunction( QStringLiteral( "wordwrap" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "delimiter" ), true, "" ), fcnWordwrap, QStringLiteral( "String" ) )
8827 << new QgsStaticExpressionFunction( QStringLiteral( "length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ), true, "" ), fcnLength, QStringList() << QStringLiteral( "String" ) << QStringLiteral( "GeometryGroup" ) )
8828 << new QgsStaticExpressionFunction( QStringLiteral( "length3D" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLength3D, QStringLiteral( "GeometryGroup" ) )
8829 << new QgsStaticExpressionFunction( QStringLiteral( "repeat" ), { QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )}, fcnRepeat, QStringLiteral( "String" ) )
8830 << new QgsStaticExpressionFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
8831 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_replace" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) )
8832 << QgsExpressionFunction::Parameter( QStringLiteral( "replacement" ) ), fcnRegexpReplace, QStringLiteral( "String" ) )
8833 << new QgsStaticExpressionFunction( QStringLiteral( "regexp_substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "input_string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpSubstr, QStringLiteral( "String" ) )
8834 << new QgsStaticExpressionFunction( QStringLiteral( "substr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(),
8835 false, QSet< QString >(), false, QStringList(), true )
8836 << new QgsStaticExpressionFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
8837 << new QgsStaticExpressionFunction( QStringLiteral( "strpos" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "haystack" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "needle" ) ), fcnStrpos, QStringLiteral( "String" ) )
8838 << new QgsStaticExpressionFunction( QStringLiteral( "left" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnLeft, QStringLiteral( "String" ) )
8839 << new QgsStaticExpressionFunction( QStringLiteral( "right" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "length" ) ), fcnRight, QStringLiteral( "String" ) )
8840 << new QgsStaticExpressionFunction( QStringLiteral( "rpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnRPad, QStringLiteral( "String" ) )
8841 << new QgsStaticExpressionFunction( QStringLiteral( "lpad" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "fill" ) ), fcnLPad, QStringLiteral( "String" ) )
8842 << new QgsStaticExpressionFunction( QStringLiteral( "format" ), -1, fcnFormatString, QStringLiteral( "String" ) )
8843 << new QgsStaticExpressionFunction( QStringLiteral( "format_number" ), QgsExpressionFunction::ParameterList()
8844 << QgsExpressionFunction::Parameter( QStringLiteral( "number" ) )
8845 << QgsExpressionFunction::Parameter( QStringLiteral( "places" ), true, 0 )
8846 << QgsExpressionFunction::Parameter( QStringLiteral( "language" ), true, QVariant() )
8847 << QgsExpressionFunction::Parameter( QStringLiteral( "omit_group_separators" ), true, false )
8848 << QgsExpressionFunction::Parameter( QStringLiteral( "trim_trailing_zeroes" ), true, false ), fcnFormatNumber, QStringLiteral( "String" ) )
8849 << 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" ) )
8850 << new QgsStaticExpressionFunction( QStringLiteral( "color_grayscale_average" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ), fcnColorGrayscaleAverage, QStringLiteral( "Color" ) )
8851 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8852 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8853 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8854 fcnColorMixRgb, QStringLiteral( "Color" ) )
8855 << new QgsStaticExpressionFunction( QStringLiteral( "color_mix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color1" ) )
8856 << QgsExpressionFunction::Parameter( QStringLiteral( "color2" ) )
8857 << QgsExpressionFunction::Parameter( QStringLiteral( "ratio" ) ),
8858 fcnColorMix, QStringLiteral( "Color" ) )
8859 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8860 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8861 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) ),
8862 fcnColorRgb, QStringLiteral( "Color" ) )
8863 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgbf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8864 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8865 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8866 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8867 fcnColorRgbF, QStringLiteral( "Color" ) )
8868 << new QgsStaticExpressionFunction( QStringLiteral( "color_rgba" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "red" ) )
8869 << QgsExpressionFunction::Parameter( QStringLiteral( "green" ) )
8870 << QgsExpressionFunction::Parameter( QStringLiteral( "blue" ) )
8871 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8872 fncColorRgba, QStringLiteral( "Color" ) )
8873 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8874 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8875 fcnRampColor, QStringLiteral( "Color" ) )
8876 << new QgsStaticExpressionFunction( QStringLiteral( "ramp_color_object" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "ramp_name" ) )
8877 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8878 fcnRampColorObject, QStringLiteral( "Color" ) )
8879 << new QgsStaticExpressionFunction( QStringLiteral( "create_ramp" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
8880 << QgsExpressionFunction::Parameter( QStringLiteral( "discrete" ), true, false ),
8881 fcnCreateRamp, QStringLiteral( "Color" ) )
8882 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8883 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8884 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) ),
8885 fcnColorHsl, QStringLiteral( "Color" ) )
8886 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsla" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8887 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8888 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8889 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8890 fncColorHsla, QStringLiteral( "Color" ) )
8891 << new QgsStaticExpressionFunction( QStringLiteral( "color_hslf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8892 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8893 << QgsExpressionFunction::Parameter( QStringLiteral( "lightness" ) )
8894 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8895 fcnColorHslF, QStringLiteral( "Color" ) )
8896 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsv" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8897 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8898 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8899 fcnColorHsv, QStringLiteral( "Color" ) )
8900 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsva" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8901 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8902 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8903 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8904 fncColorHsva, QStringLiteral( "Color" ) )
8905 << new QgsStaticExpressionFunction( QStringLiteral( "color_hsvf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "hue" ) )
8906 << QgsExpressionFunction::Parameter( QStringLiteral( "saturation" ) )
8907 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
8908 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8909 fcnColorHsvF, QStringLiteral( "Color" ) )
8910 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyk" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8911 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8912 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8913 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) ),
8914 fcnColorCmyk, QStringLiteral( "Color" ) )
8915 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmyka" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8916 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8917 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8918 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8919 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ) ),
8920 fncColorCmyka, QStringLiteral( "Color" ) )
8921 << new QgsStaticExpressionFunction( QStringLiteral( "color_cmykf" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "cyan" ) )
8922 << QgsExpressionFunction::Parameter( QStringLiteral( "magenta" ) )
8923 << QgsExpressionFunction::Parameter( QStringLiteral( "yellow" ) )
8924 << QgsExpressionFunction::Parameter( QStringLiteral( "black" ) )
8925 << QgsExpressionFunction::Parameter( QStringLiteral( "alpha" ), true, 1. ),
8926 fcnColorCmykF, QStringLiteral( "Color" ) )
8927 << new QgsStaticExpressionFunction( QStringLiteral( "color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8928 << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ),
8929 fncColorPart, QStringLiteral( "Color" ) )
8930 << new QgsStaticExpressionFunction( QStringLiteral( "darker" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8931 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8932 fncDarker, QStringLiteral( "Color" ) )
8933 << new QgsStaticExpressionFunction( QStringLiteral( "lighter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) )
8934 << QgsExpressionFunction::Parameter( QStringLiteral( "factor" ) ),
8935 fncLighter, QStringLiteral( "Color" ) )
8936 << new QgsStaticExpressionFunction( QStringLiteral( "set_color_part" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "color" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "component" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fncSetColorPart, QStringLiteral( "Color" ) )
8937
8938 // file info
8939 << new QgsStaticExpressionFunction( QStringLiteral( "base_file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8940 fcnBaseFileName, QStringLiteral( "Files and Paths" ) )
8941 << new QgsStaticExpressionFunction( QStringLiteral( "file_suffix" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8942 fcnFileSuffix, QStringLiteral( "Files and Paths" ) )
8943 << new QgsStaticExpressionFunction( QStringLiteral( "file_exists" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8944 fcnFileExists, QStringLiteral( "Files and Paths" ) )
8945 << new QgsStaticExpressionFunction( QStringLiteral( "file_name" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8946 fcnFileName, QStringLiteral( "Files and Paths" ) )
8947 << new QgsStaticExpressionFunction( QStringLiteral( "is_file" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8948 fcnPathIsFile, QStringLiteral( "Files and Paths" ) )
8949 << new QgsStaticExpressionFunction( QStringLiteral( "is_directory" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8950 fcnPathIsDir, QStringLiteral( "Files and Paths" ) )
8951 << new QgsStaticExpressionFunction( QStringLiteral( "file_path" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8952 fcnFilePath, QStringLiteral( "Files and Paths" ) )
8953 << new QgsStaticExpressionFunction( QStringLiteral( "file_size" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8954 fcnFileSize, QStringLiteral( "Files and Paths" ) )
8955
8956 << new QgsStaticExpressionFunction( QStringLiteral( "exif" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tag" ), true ),
8957 fcnExif, QStringLiteral( "Files and Paths" ) )
8958 << new QgsStaticExpressionFunction( QStringLiteral( "exif_geotag" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "path" ) ),
8959 fcnExifGeoTag, QStringLiteral( "GeometryGroup" ) )
8960
8961 // hash
8962 << new QgsStaticExpressionFunction( QStringLiteral( "hash" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "method" ) ),
8963 fcnGenericHash, QStringLiteral( "Conversions" ) )
8964 << new QgsStaticExpressionFunction( QStringLiteral( "md5" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8965 fcnHashMd5, QStringLiteral( "Conversions" ) )
8966 << new QgsStaticExpressionFunction( QStringLiteral( "sha256" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8967 fcnHashSha256, QStringLiteral( "Conversions" ) )
8968
8969 //base64
8970 << new QgsStaticExpressionFunction( QStringLiteral( "to_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ),
8971 fcnToBase64, QStringLiteral( "Conversions" ) )
8972 << new QgsStaticExpressionFunction( QStringLiteral( "from_base64" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ),
8973 fcnFromBase64, QStringLiteral( "Conversions" ) )
8974
8975 // deprecated stuff - hidden from users
8976 << new QgsStaticExpressionFunction( QStringLiteral( "$scale" ), QgsExpressionFunction::ParameterList(), fcnMapScale, QStringLiteral( "deprecated" ) );
8977
8978 QgsStaticExpressionFunction *geomFunc = new QgsStaticExpressionFunction( QStringLiteral( "$geometry" ), 0, fcnGeometry, QStringLiteral( "GeometryGroup" ), QString(), true );
8979 geomFunc->setIsStatic( false );
8980 functions << geomFunc;
8981
8982 QgsStaticExpressionFunction *areaFunc = new QgsStaticExpressionFunction( QStringLiteral( "$area" ), 0, fcnGeomArea, QStringLiteral( "GeometryGroup" ), QString(), true );
8983 areaFunc->setIsStatic( false );
8984 functions << areaFunc;
8985
8986 functions << new QgsStaticExpressionFunction( QStringLiteral( "area" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnArea, QStringLiteral( "GeometryGroup" ) );
8987
8988 QgsStaticExpressionFunction *lengthFunc = new QgsStaticExpressionFunction( QStringLiteral( "$length" ), 0, fcnGeomLength, QStringLiteral( "GeometryGroup" ), QString(), true );
8989 lengthFunc->setIsStatic( false );
8990 functions << lengthFunc;
8991
8992 QgsStaticExpressionFunction *perimeterFunc = new QgsStaticExpressionFunction( QStringLiteral( "$perimeter" ), 0, fcnGeomPerimeter, QStringLiteral( "GeometryGroup" ), QString(), true );
8993 perimeterFunc->setIsStatic( false );
8994 functions << perimeterFunc;
8995
8996 functions << new QgsStaticExpressionFunction( QStringLiteral( "perimeter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPerimeter, QStringLiteral( "GeometryGroup" ) );
8997
8998 functions << new QgsStaticExpressionFunction( QStringLiteral( "roundness" ),
8999 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9000 fcnRoundness, QStringLiteral( "GeometryGroup" ) );
9001
9002 QgsStaticExpressionFunction *xFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x" ), 0, fcnX, QStringLiteral( "GeometryGroup" ), QString(), true );
9003 xFunc->setIsStatic( false );
9004 functions << xFunc;
9005
9006 QgsStaticExpressionFunction *yFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y" ), 0, fcnY, QStringLiteral( "GeometryGroup" ), QString(), true );
9007 yFunc->setIsStatic( false );
9008 functions << yFunc;
9009
9010 QgsStaticExpressionFunction *zFunc = new QgsStaticExpressionFunction( QStringLiteral( "$z" ), 0, fcnZ, QStringLiteral( "GeometryGroup" ), QString(), true );
9011 zFunc->setIsStatic( false );
9012 functions << zFunc;
9013
9014 QMap< QString, QgsExpressionFunction::FcnEval > geometry_overlay_definitions
9015 {
9016 { QStringLiteral( "overlay_intersects" ), fcnGeomOverlayIntersects },
9017 { QStringLiteral( "overlay_contains" ), fcnGeomOverlayContains },
9018 { QStringLiteral( "overlay_crosses" ), fcnGeomOverlayCrosses },
9019 { QStringLiteral( "overlay_equals" ), fcnGeomOverlayEquals },
9020 { QStringLiteral( "overlay_touches" ), fcnGeomOverlayTouches },
9021 { QStringLiteral( "overlay_disjoint" ), fcnGeomOverlayDisjoint },
9022 { QStringLiteral( "overlay_within" ), fcnGeomOverlayWithin },
9023 };
9024 QMapIterator< QString, QgsExpressionFunction::FcnEval > i( geometry_overlay_definitions );
9025 while ( i.hasNext() )
9026 {
9027 i.next();
9028 QgsStaticExpressionFunction *fcnGeomOverlayFunc = new QgsStaticExpressionFunction( i.key(), QgsExpressionFunction::ParameterList()
9029 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9030 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
9031 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
9032 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( -1 ), true )
9033 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false )
9034 << QgsExpressionFunction::Parameter( QStringLiteral( "min_overlap" ), true, QVariant( -1 ), false )
9035 << QgsExpressionFunction::Parameter( QStringLiteral( "min_inscribed_circle_radius" ), true, QVariant( -1 ), false )
9036 << QgsExpressionFunction::Parameter( QStringLiteral( "return_details" ), true, false, false )
9037 << QgsExpressionFunction::Parameter( QStringLiteral( "sort_by_intersection_size" ), true, QString(), false ),
9038 i.value(), QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
9039
9040 // The current feature is accessed for the geometry, so this should not be cached
9041 fcnGeomOverlayFunc->setIsStatic( false );
9042 functions << fcnGeomOverlayFunc;
9043 }
9044
9045 QgsStaticExpressionFunction *fcnGeomOverlayNearestFunc = new QgsStaticExpressionFunction( QStringLiteral( "overlay_nearest" ), QgsExpressionFunction::ParameterList()
9046 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9047 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ), true, QVariant(), true )
9048 << QgsExpressionFunction::Parameter( QStringLiteral( "filter" ), true, QVariant(), true )
9049 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, QVariant( 1 ), true )
9050 << QgsExpressionFunction::Parameter( QStringLiteral( "max_distance" ), true, 0 )
9051 << QgsExpressionFunction::Parameter( QStringLiteral( "cache" ), true, QVariant( false ), false ),
9052 fcnGeomOverlayNearest, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES, true );
9053 // The current feature is accessed for the geometry, so this should not be cached
9054 fcnGeomOverlayNearestFunc->setIsStatic( false );
9055 functions << fcnGeomOverlayNearestFunc;
9056
9057 functions
9058 << new QgsStaticExpressionFunction( QStringLiteral( "is_valid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomIsValid, QStringLiteral( "GeometryGroup" ) )
9059 << new QgsStaticExpressionFunction( QStringLiteral( "x" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomX, QStringLiteral( "GeometryGroup" ) )
9060 << new QgsStaticExpressionFunction( QStringLiteral( "y" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomY, QStringLiteral( "GeometryGroup" ) )
9061 << new QgsStaticExpressionFunction( QStringLiteral( "z" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomZ, QStringLiteral( "GeometryGroup" ) )
9062 << new QgsStaticExpressionFunction( QStringLiteral( "m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomM, QStringLiteral( "GeometryGroup" ) )
9063 << new QgsStaticExpressionFunction( QStringLiteral( "point_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ), fcnPointN, QStringLiteral( "GeometryGroup" ) )
9064 << new QgsStaticExpressionFunction( QStringLiteral( "start_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnStartPoint, QStringLiteral( "GeometryGroup" ) )
9065 << new QgsStaticExpressionFunction( QStringLiteral( "end_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnEndPoint, QStringLiteral( "GeometryGroup" ) )
9066 << new QgsStaticExpressionFunction( QStringLiteral( "nodes_to_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9067 << QgsExpressionFunction::Parameter( QStringLiteral( "ignore_closing_nodes" ), true, false ),
9068 fcnNodesToPoints, QStringLiteral( "GeometryGroup" ) )
9069 << new QgsStaticExpressionFunction( QStringLiteral( "segments_to_lines" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnSegmentsToLines, QStringLiteral( "GeometryGroup" ) )
9070 << new QgsStaticExpressionFunction( QStringLiteral( "collect_geometries" ), -1, fcnCollectGeometries, QStringLiteral( "GeometryGroup" ) )
9071 << new QgsStaticExpressionFunction( QStringLiteral( "make_point" ), -1, fcnMakePoint, QStringLiteral( "GeometryGroup" ) )
9072 << new QgsStaticExpressionFunction( QStringLiteral( "make_point_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9073 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) )
9074 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ),
9075 fcnMakePointM, QStringLiteral( "GeometryGroup" ) )
9076 << new QgsStaticExpressionFunction( QStringLiteral( "make_line" ), -1, fcnMakeLine, QStringLiteral( "GeometryGroup" ) )
9077 << new QgsStaticExpressionFunction( QStringLiteral( "make_polygon" ), -1, fcnMakePolygon, QStringLiteral( "GeometryGroup" ) )
9078 << new QgsStaticExpressionFunction( QStringLiteral( "make_triangle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
9079 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
9080 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) ),
9081 fcnMakeTriangle, QStringLiteral( "GeometryGroup" ) )
9082 << new QgsStaticExpressionFunction( QStringLiteral( "make_circle" ), QgsExpressionFunction::ParameterList()
9083 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9084 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
9085 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9086 fcnMakeCircle, QStringLiteral( "GeometryGroup" ) )
9087 << new QgsStaticExpressionFunction( QStringLiteral( "make_ellipse" ), QgsExpressionFunction::ParameterList()
9088 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9089 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_major_axis" ) )
9090 << QgsExpressionFunction::Parameter( QStringLiteral( "semi_minor_axis" ) )
9091 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
9092 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9093 fcnMakeEllipse, QStringLiteral( "GeometryGroup" ) )
9094 << new QgsStaticExpressionFunction( QStringLiteral( "make_regular_polygon" ), QgsExpressionFunction::ParameterList()
9095 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9096 << QgsExpressionFunction::Parameter( QStringLiteral( "radius" ) )
9097 << QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
9098 << QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
9099 fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
9100 << new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
9101 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
9102 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
9103 fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
9104 << new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
9105 << QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
9106 << QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
9107 << QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
9108 << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
9109 fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) )
9110 << new QgsStaticExpressionFunction( QStringLiteral( "make_valid" ), QgsExpressionFunction::ParameterList
9111 {
9112 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9113#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
9114 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "linework" ) ),
9115#else
9116 QgsExpressionFunction::Parameter( QStringLiteral( "method" ), true, QStringLiteral( "structure" ) ),
9117#endif
9118 QgsExpressionFunction::Parameter( QStringLiteral( "keep_collapsed" ), true, false )
9119 }, fcnGeomMakeValid, QStringLiteral( "GeometryGroup" ) );
9120
9121 functions << new QgsStaticExpressionFunction( QStringLiteral( "x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnXat, QStringLiteral( "GeometryGroup" ) );
9122 functions << new QgsStaticExpressionFunction( QStringLiteral( "y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ), true ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnYat, QStringLiteral( "GeometryGroup" ) );
9123 functions << new QgsStaticExpressionFunction( QStringLiteral( "z_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnZat, QStringLiteral( "GeometryGroup" ) );
9124 functions << new QgsStaticExpressionFunction( QStringLiteral( "m_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ), true ), fcnMat, QStringLiteral( "GeometryGroup" ) );
9125
9126 QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) );
9127 xAtFunc->setIsStatic( false );
9128 functions << xAtFunc;
9129
9130
9131 QgsStaticExpressionFunction *yAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$y_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnOldYat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "yat" ) );
9132 yAtFunc->setIsStatic( false );
9133 functions << yAtFunc;
9134
9135 functions
9136 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_type" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeometryType, QStringLiteral( "GeometryGroup" ) )
9137 << new QgsStaticExpressionFunction( QStringLiteral( "x_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmin" ) )
9138 << new QgsStaticExpressionFunction( QStringLiteral( "x_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnXMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "xmax" ) )
9139 << new QgsStaticExpressionFunction( QStringLiteral( "y_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMin, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymin" ) )
9140 << new QgsStaticExpressionFunction( QStringLiteral( "y_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnYMax, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "ymax" ) )
9141 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "text" ) ), fcnGeomFromWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromWKT" ) )
9142 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "binary" ) ), fcnGeomFromWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9143 << new QgsStaticExpressionFunction( QStringLiteral( "geom_from_gml" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "gml" ) ), fcnGeomFromGML, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomFromGML" ) )
9144 << new QgsStaticExpressionFunction( QStringLiteral( "flip_coordinates" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnFlipCoordinates, QStringLiteral( "GeometryGroup" ) )
9145 << new QgsStaticExpressionFunction( QStringLiteral( "relate" ), -1, fcnRelate, QStringLiteral( "GeometryGroup" ) )
9146 << 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" ) )
9147 << new QgsStaticExpressionFunction( QStringLiteral( "disjoint" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9148 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9149 fcnDisjoint, QStringLiteral( "GeometryGroup" ) )
9150 << new QgsStaticExpressionFunction( QStringLiteral( "intersects" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9151 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9152 fcnIntersects, QStringLiteral( "GeometryGroup" ) )
9153 << new QgsStaticExpressionFunction( QStringLiteral( "touches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9154 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9155 fcnTouches, QStringLiteral( "GeometryGroup" ) )
9156 << new QgsStaticExpressionFunction( QStringLiteral( "crosses" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9157 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9158 fcnCrosses, QStringLiteral( "GeometryGroup" ) )
9159 << new QgsStaticExpressionFunction( QStringLiteral( "contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9160 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9161 fcnContains, QStringLiteral( "GeometryGroup" ) )
9162 << new QgsStaticExpressionFunction( QStringLiteral( "overlaps" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9163 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9164 fcnOverlaps, QStringLiteral( "GeometryGroup" ) )
9165 << new QgsStaticExpressionFunction( QStringLiteral( "within" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9166 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9167 fcnWithin, QStringLiteral( "GeometryGroup" ) )
9168 << new QgsStaticExpressionFunction( QStringLiteral( "translate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9169 << QgsExpressionFunction::Parameter( QStringLiteral( "dx" ) )
9170 << QgsExpressionFunction::Parameter( QStringLiteral( "dy" ) ),
9171 fcnTranslate, QStringLiteral( "GeometryGroup" ) )
9172 << new QgsStaticExpressionFunction( QStringLiteral( "rotate" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9173 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
9174 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true )
9175 << QgsExpressionFunction::Parameter( QStringLiteral( "per_part" ), true, false ),
9176 fcnRotate, QStringLiteral( "GeometryGroup" ) )
9177 << new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9178 << QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
9179 << QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
9180 << QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
9181 fcnScale, QStringLiteral( "GeometryGroup" ) )
9182 << new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9183 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
9184 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
9185 << QgsExpressionFunction::Parameter( QStringLiteral( "rotation_z" ) )
9186 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_x" ) )
9187 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_y" ) )
9188 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_z" ), true, 0 )
9189 << QgsExpressionFunction::Parameter( QStringLiteral( "delta_m" ), true, 0 )
9190 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_z" ), true, 1 )
9191 << QgsExpressionFunction::Parameter( QStringLiteral( "scale_m" ), true, 1 ),
9192 fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
9193 << new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9194 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9195 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 )
9196 << QgsExpressionFunction::Parameter( QStringLiteral( "cap" ), true, QStringLiteral( "round" ) )
9197 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, QStringLiteral( "round" ) )
9198 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2 ),
9199 fcnBuffer, QStringLiteral( "GeometryGroup" ) )
9200 << new QgsStaticExpressionFunction( QStringLiteral( "force_rhr" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9201 fcnForceRHR, QStringLiteral( "GeometryGroup" ) )
9202 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_cw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9203 fcnForcePolygonCW, QStringLiteral( "GeometryGroup" ) )
9204 << new QgsStaticExpressionFunction( QStringLiteral( "force_polygon_ccw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9205 fcnForcePolygonCCW, QStringLiteral( "GeometryGroup" ) )
9206 << new QgsStaticExpressionFunction( QStringLiteral( "wedge_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "center" ) )
9207 << QgsExpressionFunction::Parameter( QStringLiteral( "azimuth" ) )
9208 << QgsExpressionFunction::Parameter( QStringLiteral( "width" ) )
9209 << QgsExpressionFunction::Parameter( QStringLiteral( "outer_radius" ) )
9210 << QgsExpressionFunction::Parameter( QStringLiteral( "inner_radius" ), true, 0.0 ), fcnWedgeBuffer, QStringLiteral( "GeometryGroup" ) )
9211 << new QgsStaticExpressionFunction( QStringLiteral( "tapered_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9212 << QgsExpressionFunction::Parameter( QStringLiteral( "start_width" ) )
9213 << QgsExpressionFunction::Parameter( QStringLiteral( "end_width" ) )
9214 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9215 , fcnTaperedBuffer, QStringLiteral( "GeometryGroup" ) )
9216 << new QgsStaticExpressionFunction( QStringLiteral( "buffer_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9217 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9218 , fcnBufferByM, QStringLiteral( "GeometryGroup" ) )
9219 << new QgsStaticExpressionFunction( QStringLiteral( "offset_curve" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9220 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9221 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9222 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9223 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9224 fcnOffsetCurve, QStringLiteral( "GeometryGroup" ) )
9225 << new QgsStaticExpressionFunction( QStringLiteral( "single_sided_buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9226 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9227 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8.0 )
9228 << QgsExpressionFunction::Parameter( QStringLiteral( "join" ), true, static_cast< int >( Qgis::JoinStyle::Round ) )
9229 << QgsExpressionFunction::Parameter( QStringLiteral( "miter_limit" ), true, 2.0 ),
9230 fcnSingleSidedBuffer, QStringLiteral( "GeometryGroup" ) )
9231 << new QgsStaticExpressionFunction( QStringLiteral( "extend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9232 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) )
9233 << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ),
9234 fcnExtend, QStringLiteral( "GeometryGroup" ) )
9235 << new QgsStaticExpressionFunction( QStringLiteral( "centroid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCentroid, QStringLiteral( "GeometryGroup" ) )
9236 << new QgsStaticExpressionFunction( QStringLiteral( "point_on_surface" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnPointOnSurface, QStringLiteral( "GeometryGroup" ) )
9237 << new QgsStaticExpressionFunction( QStringLiteral( "pole_of_inaccessibility" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9238 << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnPoleOfInaccessibility, QStringLiteral( "GeometryGroup" ) )
9239 << new QgsStaticExpressionFunction( QStringLiteral( "reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnReverse, { QStringLiteral( "String" ), QStringLiteral( "GeometryGroup" ) } )
9240 << new QgsStaticExpressionFunction( QStringLiteral( "exterior_ring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnExteriorRing, QStringLiteral( "GeometryGroup" ) )
9241 << new QgsStaticExpressionFunction( QStringLiteral( "interior_ring_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9242 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9243 fcnInteriorRingN, QStringLiteral( "GeometryGroup" ) )
9244 << new QgsStaticExpressionFunction( QStringLiteral( "geometry_n" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9245 << QgsExpressionFunction::Parameter( QStringLiteral( "index" ) ),
9246 fcnGeometryN, QStringLiteral( "GeometryGroup" ) )
9247 << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) )
9248 << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) )
9249 << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList
9250 {
9251 QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ),
9252 QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9253 }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) )
9254 << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) )
9255 << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) )
9256 << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) )
9257 << new QgsStaticExpressionFunction( QStringLiteral( "smooth" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "iterations" ), true, 1 )
9258 << QgsExpressionFunction::Parameter( QStringLiteral( "offset" ), true, 0.25 )
9259 << QgsExpressionFunction::Parameter( QStringLiteral( "min_length" ), true, -1 )
9260 << QgsExpressionFunction::Parameter( QStringLiteral( "max_angle" ), true, 180 ), fcnSmooth, QStringLiteral( "GeometryGroup" ) )
9261 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave" ),
9262 {
9263 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9264 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9265 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9266 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9267 }, fcnTriangularWave, QStringLiteral( "GeometryGroup" ) )
9268 << new QgsStaticExpressionFunction( QStringLiteral( "triangular_wave_randomized" ),
9269 {
9270 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9271 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9272 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9273 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9274 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9275 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9276 }, fcnTriangularWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9277 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave" ),
9278 {
9279 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9280 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9281 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9282 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9283 }, fcnSquareWave, QStringLiteral( "GeometryGroup" ) )
9284 << new QgsStaticExpressionFunction( QStringLiteral( "square_wave_randomized" ),
9285 {
9286 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9287 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9288 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9289 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9290 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9291 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9292 }, fcnSquareWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9293 << new QgsStaticExpressionFunction( QStringLiteral( "wave" ),
9294 {
9295 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9296 QgsExpressionFunction::Parameter( QStringLiteral( "wavelength" ) ),
9297 QgsExpressionFunction::Parameter( QStringLiteral( "amplitude" ) ),
9298 QgsExpressionFunction::Parameter( QStringLiteral( "strict" ), true, false )
9299 }, fcnRoundWave, QStringLiteral( "GeometryGroup" ) )
9300 << new QgsStaticExpressionFunction( QStringLiteral( "wave_randomized" ),
9301 {
9302 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9303 QgsExpressionFunction::Parameter( QStringLiteral( "min_wavelength" ) ),
9304 QgsExpressionFunction::Parameter( QStringLiteral( "max_wavelength" ) ),
9305 QgsExpressionFunction::Parameter( QStringLiteral( "min_amplitude" ) ),
9306 QgsExpressionFunction::Parameter( QStringLiteral( "max_amplitude" ) ),
9307 QgsExpressionFunction::Parameter( QStringLiteral( "seed" ), true, 0 )
9308 }, fcnRoundWaveRandomized, QStringLiteral( "GeometryGroup" ) )
9309 << new QgsStaticExpressionFunction( QStringLiteral( "apply_dash_pattern" ),
9310 {
9311 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9312 QgsExpressionFunction::Parameter( QStringLiteral( "pattern" ) ),
9313 QgsExpressionFunction::Parameter( QStringLiteral( "start_rule" ), true, QStringLiteral( "no_rule" ) ),
9314 QgsExpressionFunction::Parameter( QStringLiteral( "end_rule" ), true, QStringLiteral( "no_rule" ) ),
9315 QgsExpressionFunction::Parameter( QStringLiteral( "adjustment" ), true, QStringLiteral( "both" ) ),
9316 QgsExpressionFunction::Parameter( QStringLiteral( "pattern_offset" ), true, 0 ),
9317 }, fcnApplyDashPattern, QStringLiteral( "GeometryGroup" ) )
9318 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_count" ),
9319 {
9320 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9321 QgsExpressionFunction::Parameter( QStringLiteral( "vertices" ) )
9322 }, fcnDensifyByCount, QStringLiteral( "GeometryGroup" ) )
9323 << new QgsStaticExpressionFunction( QStringLiteral( "densify_by_distance" ),
9324 {
9325 QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9326 QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
9327 }, fcnDensifyByDistance, QStringLiteral( "GeometryGroup" ) )
9328 << new QgsStaticExpressionFunction( QStringLiteral( "num_points" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumPoints, QStringLiteral( "GeometryGroup" ) )
9329 << new QgsStaticExpressionFunction( QStringLiteral( "num_interior_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumInteriorRings, QStringLiteral( "GeometryGroup" ) )
9330 << new QgsStaticExpressionFunction( QStringLiteral( "num_rings" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumRings, QStringLiteral( "GeometryGroup" ) )
9331 << new QgsStaticExpressionFunction( QStringLiteral( "num_geometries" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnGeomNumGeometries, QStringLiteral( "GeometryGroup" ) )
9332 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_width" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsWidth, QStringLiteral( "GeometryGroup" ) )
9333 << new QgsStaticExpressionFunction( QStringLiteral( "bounds_height" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundsHeight, QStringLiteral( "GeometryGroup" ) )
9334 << new QgsStaticExpressionFunction( QStringLiteral( "is_closed" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsClosed, QStringLiteral( "GeometryGroup" ) )
9335 << new QgsStaticExpressionFunction( QStringLiteral( "close_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnCloseLine, QStringLiteral( "GeometryGroup" ) )
9336 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmpty, QStringLiteral( "GeometryGroup" ) )
9337 << new QgsStaticExpressionFunction( QStringLiteral( "is_empty_or_null" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnIsEmptyOrNull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9338 << new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
9339#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=11 )
9340 << new QgsStaticExpressionFunction( QStringLiteral( "concave_hull" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9341 << QgsExpressionFunction::Parameter( QStringLiteral( "target_percent" ) )
9342 << QgsExpressionFunction::Parameter( QStringLiteral( "allow_holes" ), true, false ), fcnConcaveHull, QStringLiteral( "GeometryGroup" ) )
9343#endif
9344 << new QgsStaticExpressionFunction( QStringLiteral( "oriented_bbox" ), QgsExpressionFunction::ParameterList()
9345 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9346 fcnOrientedBBox, QStringLiteral( "GeometryGroup" ) )
9347 << new QgsStaticExpressionFunction( QStringLiteral( "main_angle" ), QgsExpressionFunction::ParameterList()
9348 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9349 fcnMainAngle, QStringLiteral( "GeometryGroup" ) )
9350 << new QgsStaticExpressionFunction( QStringLiteral( "minimal_circle" ), QgsExpressionFunction::ParameterList()
9351 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9352 << QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 36 ),
9353 fcnMinimalCircle, QStringLiteral( "GeometryGroup" ) )
9354 << new QgsStaticExpressionFunction( QStringLiteral( "difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9355 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9356 fcnDifference, QStringLiteral( "GeometryGroup" ) )
9357 << new QgsStaticExpressionFunction( QStringLiteral( "distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9358 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9359 fcnDistance, QStringLiteral( "GeometryGroup" ) )
9360 << new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
9361 << QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ),
9362 fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
9363 << new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9364 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9365 fcnIntersection, QStringLiteral( "GeometryGroup" ) )
9366 << new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9367 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9368 fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
9369 << new QgsStaticExpressionFunction( QStringLiteral( "combine" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9370 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9371 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9372 << new QgsStaticExpressionFunction( QStringLiteral( "union" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9373 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9374 fcnCombine, QStringLiteral( "GeometryGroup" ) )
9375 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkt" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9376 << QgsExpressionFunction::Parameter( QStringLiteral( "precision" ), true, 8.0 ),
9377 fcnGeomToWKT, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "geomToWKT" ) )
9378 << new QgsStaticExpressionFunction( QStringLiteral( "geom_to_wkb" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9379 fcnGeomToWKB, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false )
9380 << new QgsStaticExpressionFunction( QStringLiteral( "geometry" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetGeometry, QStringLiteral( "GeometryGroup" ), QString(), true )
9381 << new QgsStaticExpressionFunction( QStringLiteral( "transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9382 << QgsExpressionFunction::Parameter( QStringLiteral( "source_auth_id" ) )
9383 << QgsExpressionFunction::Parameter( QStringLiteral( "dest_auth_id" ) ),
9384 fcnTransformGeometry, QStringLiteral( "GeometryGroup" ) )
9385 << new QgsStaticExpressionFunction( QStringLiteral( "extrude" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9386 << QgsExpressionFunction::Parameter( QStringLiteral( "x" ) )
9387 << QgsExpressionFunction::Parameter( QStringLiteral( "y" ) ),
9388 fcnExtrude, QStringLiteral( "GeometryGroup" ), QString() )
9389 << new QgsStaticExpressionFunction( QStringLiteral( "is_multipart" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9390 fcnGeomIsMultipart, QStringLiteral( "GeometryGroup" ) )
9391 << new QgsStaticExpressionFunction( QStringLiteral( "z_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9392 fcnZMax, QStringLiteral( "GeometryGroup" ) )
9393 << new QgsStaticExpressionFunction( QStringLiteral( "z_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9394 fcnZMin, QStringLiteral( "GeometryGroup" ) )
9395 << new QgsStaticExpressionFunction( QStringLiteral( "m_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9396 fcnMMax, QStringLiteral( "GeometryGroup" ) )
9397 << new QgsStaticExpressionFunction( QStringLiteral( "m_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9398 fcnMMin, QStringLiteral( "GeometryGroup" ) )
9399 << new QgsStaticExpressionFunction( QStringLiteral( "sinuosity" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9400 fcnSinuosity, QStringLiteral( "GeometryGroup" ) )
9401 << new QgsStaticExpressionFunction( QStringLiteral( "straight_distance_2d" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ),
9402 fcnStraightDistance2d, QStringLiteral( "GeometryGroup" ) );
9403
9404
9405 QgsStaticExpressionFunction *orderPartsFunc = new QgsStaticExpressionFunction( QStringLiteral( "order_parts" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9406 << QgsExpressionFunction::Parameter( QStringLiteral( "orderby" ) )
9407 << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ),
9408 fcnOrderParts, QStringLiteral( "GeometryGroup" ), QString() );
9409
9410 orderPartsFunc->setIsStaticFunction(
9411 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9412 {
9413 const QList< QgsExpressionNode *> argList = node->args()->list();
9414 for ( QgsExpressionNode *argNode : argList )
9415 {
9416 if ( !argNode->isStatic( parent, context ) )
9417 return false;
9418 }
9419
9420 if ( node->args()->count() > 1 )
9421 {
9422 QgsExpressionNode *argNode = node->args()->at( 1 );
9423
9424 QString expString = argNode->eval( parent, context ).toString();
9425
9426 QgsExpression e( expString );
9427
9428 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9429 return true;
9430 }
9431
9432 return true;
9433 } );
9434
9435 orderPartsFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9436 {
9437 if ( node->args()->count() > 1 )
9438 {
9439 QgsExpressionNode *argNode = node->args()->at( 1 );
9440 QString expression = argNode->eval( parent, context ).toString();
9442 e.prepare( context );
9443 context->setCachedValue( expression, QVariant::fromValue( e ) );
9444 }
9445 return true;
9446 }
9447 );
9448 functions << orderPartsFunc;
9449
9450 functions
9451 << new QgsStaticExpressionFunction( QStringLiteral( "closest_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9452 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9453 fcnClosestPoint, QStringLiteral( "GeometryGroup" ) )
9454 << new QgsStaticExpressionFunction( QStringLiteral( "shortest_line" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) )
9455 << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ),
9456 fcnShortestLine, QStringLiteral( "GeometryGroup" ) )
9457 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9458 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolatePoint, QStringLiteral( "GeometryGroup" ) )
9459 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_point_by_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9460 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9461 fcnLineInterpolatePointByM, QStringLiteral( "GeometryGroup" ) )
9462 << new QgsStaticExpressionFunction( QStringLiteral( "line_interpolate_angle" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9463 << QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) ), fcnLineInterpolateAngle, QStringLiteral( "GeometryGroup" ) )
9464 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_point" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9465 << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnLineLocatePoint, QStringLiteral( "GeometryGroup" ) )
9466 << new QgsStaticExpressionFunction( QStringLiteral( "line_locate_m" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9467 << QgsExpressionFunction::Parameter( QStringLiteral( "m" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "use_3d_distance" ), true, false ),
9468 fcnLineLocateM, QStringLiteral( "GeometryGroup" ) )
9469 << new QgsStaticExpressionFunction( QStringLiteral( "angle_at_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9470 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnAngleAtVertex, QStringLiteral( "GeometryGroup" ) )
9471 << new QgsStaticExpressionFunction( QStringLiteral( "distance_to_vertex" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9472 << QgsExpressionFunction::Parameter( QStringLiteral( "vertex" ) ), fcnDistanceToVertex, QStringLiteral( "GeometryGroup" ) )
9473 << new QgsStaticExpressionFunction( QStringLiteral( "line_substring" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
9474 << QgsExpressionFunction::Parameter( QStringLiteral( "start_distance" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_distance" ) ), fcnLineSubset, QStringLiteral( "GeometryGroup" ) );
9475
9476
9477 // **Record** functions
9478
9479 QgsStaticExpressionFunction *idFunc = new QgsStaticExpressionFunction( QStringLiteral( "$id" ), 0, fcnFeatureId, QStringLiteral( "Record and Attributes" ) );
9480 idFunc->setIsStatic( false );
9481 functions << idFunc;
9482
9483 QgsStaticExpressionFunction *currentFeatureFunc = new QgsStaticExpressionFunction( QStringLiteral( "$currentfeature" ), 0, fcnFeature, QStringLiteral( "Record and Attributes" ) );
9484 currentFeatureFunc->setIsStatic( false );
9485 functions << currentFeatureFunc;
9486
9487 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" ) );
9488 uuidFunc->setIsStatic( false );
9489 functions << uuidFunc;
9490
9491 functions
9492 << new QgsStaticExpressionFunction( QStringLiteral( "feature_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ) ), fcnGetFeatureId, QStringLiteral( "Record and Attributes" ), QString(), true )
9493 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9494 << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) )
9495 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ), true ),
9496 fcnGetFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "QgsExpressionUtils::getFeature" ) )
9497 << new QgsStaticExpressionFunction( QStringLiteral( "get_feature_by_id" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9498 << QgsExpressionFunction::Parameter( QStringLiteral( "feature_id" ) ),
9499 fcnGetFeatureById, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>(), false );
9500
9501 QgsStaticExpressionFunction *attributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true ),
9502 fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9503 attributesFunc->setIsStatic( false );
9504 functions << attributesFunc;
9505 QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
9506 fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9507 representAttributesFunc->setIsStatic( false );
9508 functions << representAttributesFunc;
9509
9510 QgsStaticExpressionFunction *validateFeature = new QgsStaticExpressionFunction( QStringLiteral( "is_feature_valid" ),
9511 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9512 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9513 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9514 fcnValidateFeature, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9515 validateFeature->setIsStatic( false );
9516 functions << validateFeature;
9517
9518 QgsStaticExpressionFunction *validateAttribute = new QgsStaticExpressionFunction( QStringLiteral( "is_attribute_valid" ),
9519 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ), false )
9520 << QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ), true )
9521 << QgsExpressionFunction::Parameter( QStringLiteral( "feature" ), true )
9522 << QgsExpressionFunction::Parameter( QStringLiteral( "strength" ), true ),
9523 fcnValidateAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9524 validateAttribute->setIsStatic( false );
9525 functions << validateAttribute;
9526
9527 QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction(
9528 QStringLiteral( "maptip" ),
9529 -1,
9530 fcnFeatureMaptip,
9531 QStringLiteral( "Record and Attributes" ),
9532 QString(),
9533 false,
9534 QSet<QString>()
9535 );
9536 maptipFunc->setIsStatic( false );
9537 functions << maptipFunc;
9538
9539 QgsStaticExpressionFunction *displayFunc = new QgsStaticExpressionFunction(
9540 QStringLiteral( "display_expression" ),
9541 -1,
9542 fcnFeatureDisplayExpression,
9543 QStringLiteral( "Record and Attributes" ),
9544 QString(),
9545 false,
9546 QSet<QString>()
9547 );
9548 displayFunc->setIsStatic( false );
9549 functions << displayFunc;
9550
9551 QgsStaticExpressionFunction *isSelectedFunc = new QgsStaticExpressionFunction(
9552 QStringLiteral( "is_selected" ),
9553 -1,
9554 fcnIsSelected,
9555 QStringLiteral( "Record and Attributes" ),
9556 QString(),
9557 false,
9558 QSet<QString>()
9559 );
9560 isSelectedFunc->setIsStatic( false );
9561 functions << isSelectedFunc;
9562
9563 functions
9564 << new QgsStaticExpressionFunction(
9565 QStringLiteral( "num_selected" ),
9566 -1,
9567 fcnNumSelected,
9568 QStringLiteral( "Record and Attributes" ),
9569 QString(),
9570 false,
9571 QSet<QString>()
9572 );
9573
9574 functions
9575 << new QgsStaticExpressionFunction(
9576 QStringLiteral( "sqlite_fetch_and_increment" ),
9578 << QgsExpressionFunction::Parameter( QStringLiteral( "database" ) )
9579 << QgsExpressionFunction::Parameter( QStringLiteral( "table" ) )
9580 << QgsExpressionFunction::Parameter( QStringLiteral( "id_field" ) )
9581 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_attribute" ) )
9582 << QgsExpressionFunction::Parameter( QStringLiteral( "filter_value" ) )
9583 << QgsExpressionFunction::Parameter( QStringLiteral( "default_values" ), true ),
9584 fcnSqliteFetchAndIncrement,
9585 QStringLiteral( "Record and Attributes" )
9586 );
9587
9588 // **CRS** functions
9589 functions
9590 << new QgsStaticExpressionFunction( QStringLiteral( "crs_to_authid" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "crs" ) ), fcnCrsToAuthid, QStringLiteral( "CRS" ), QString(), true )
9591 << new QgsStaticExpressionFunction( QStringLiteral( "crs_from_text" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "definition" ) ), fcnCrsFromText, QStringLiteral( "CRS" ) );
9592
9593
9594 // **Fields and Values** functions
9595 QgsStaticExpressionFunction *representValueFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "attribute" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "field_name" ), true ), fcnRepresentValue, QStringLiteral( "Record and Attributes" ) );
9596
9597 representValueFunc->setPrepareFunction( []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9598 {
9599 Q_UNUSED( context )
9600 if ( node->args()->count() == 1 )
9601 {
9602 QgsExpressionNodeColumnRef *colRef = dynamic_cast<QgsExpressionNodeColumnRef *>( node->args()->at( 0 ) );
9603 if ( colRef )
9604 {
9605 return true;
9606 }
9607 else
9608 {
9609 parent->setEvalErrorString( tr( "If represent_value is called with 1 parameter, it must be an attribute." ) );
9610 return false;
9611 }
9612 }
9613 else if ( node->args()->count() == 2 )
9614 {
9615 return true;
9616 }
9617 else
9618 {
9619 parent->setEvalErrorString( tr( "represent_value must be called with exactly 1 or 2 parameters." ) );
9620 return false;
9621 }
9622 }
9623 );
9624
9625 functions << representValueFunc;
9626
9627 // **General** functions
9628 functions
9629 << new QgsStaticExpressionFunction( QStringLiteral( "layer_property" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9630 << QgsExpressionFunction::Parameter( QStringLiteral( "property" ) ),
9631 fcnGetLayerProperty, QStringLiteral( "Map Layers" ) )
9632 << new QgsStaticExpressionFunction( QStringLiteral( "decode_uri" ),
9634 << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9635 << QgsExpressionFunction::Parameter( QStringLiteral( "part" ), true ),
9636 fcnDecodeUri, QStringLiteral( "Map Layers" ) )
9637 << new QgsStaticExpressionFunction( QStringLiteral( "mime_type" ),
9639 << QgsExpressionFunction::Parameter( QStringLiteral( "binary_data" ) ),
9640 fcnMimeType, QStringLiteral( "General" ) )
9641 << new QgsStaticExpressionFunction( QStringLiteral( "raster_statistic" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) )
9642 << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) )
9643 << QgsExpressionFunction::Parameter( QStringLiteral( "statistic" ) ), fcnGetRasterBandStat, QStringLiteral( "Rasters" ) );
9644
9645 // **var** function
9646 QgsStaticExpressionFunction *varFunction = new QgsStaticExpressionFunction( QStringLiteral( "var" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnGetVariable, QStringLiteral( "General" ) );
9647 varFunction->setIsStaticFunction(
9648 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9649 {
9650 /* A variable node is static if it has a static name and the name can be found at prepare
9651 * time and is tagged with isStatic.
9652 * It is not static if a variable is set during iteration or not tagged isStatic.
9653 * (e.g. geom_part variable)
9654 */
9655 if ( node->args()->count() > 0 )
9656 {
9657 QgsExpressionNode *argNode = node->args()->at( 0 );
9658
9659 if ( !argNode->isStatic( parent, context ) )
9660 return false;
9661
9662 const QString varName = argNode->eval( parent, context ).toString();
9663 if ( varName == QLatin1String( "feature" ) || varName == QLatin1String( "id" ) || varName == QLatin1String( "geometry" ) )
9664 return false;
9665
9666 const QgsExpressionContextScope *scope = context->activeScopeForVariable( varName );
9667 return scope ? scope->isStatic( varName ) : false;
9668 }
9669 return false;
9670 }
9671 );
9672 varFunction->setUsesGeometryFunction(
9673 []( const QgsExpressionNodeFunction * node ) -> bool
9674 {
9675 if ( node && node->args()->count() > 0 )
9676 {
9677 QgsExpressionNode *argNode = node->args()->at( 0 );
9678 if ( QgsExpressionNodeLiteral *literal = dynamic_cast<QgsExpressionNodeLiteral *>( argNode ) )
9679 {
9680 if ( literal->value() == QLatin1String( "geometry" ) || literal->value() == QLatin1String( "feature" ) )
9681 return true;
9682 }
9683 }
9684 return false;
9685 }
9686 );
9687
9688 functions
9689 << varFunction;
9690
9691 QgsStaticExpressionFunction *evalTemplateFunction = new QgsStaticExpressionFunction( QStringLiteral( "eval_template" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "template" ) ), fcnEvalTemplate, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9692 evalTemplateFunction->setIsStaticFunction(
9693 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9694 {
9695 if ( node->args()->count() > 0 )
9696 {
9697 QgsExpressionNode *argNode = node->args()->at( 0 );
9698
9699 if ( argNode->isStatic( parent, context ) )
9700 {
9701 QString expString = argNode->eval( parent, context ).toString();
9702
9703 QgsExpression e( expString );
9704
9705 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9706 return true;
9707 }
9708 }
9709
9710 return false;
9711 } );
9712 functions << evalTemplateFunction;
9713
9714 QgsStaticExpressionFunction *evalFunc = new QgsStaticExpressionFunction( QStringLiteral( "eval" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ), fcnEval, QStringLiteral( "General" ), QString(), true, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9715 evalFunc->setIsStaticFunction(
9716 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9717 {
9718 if ( node->args()->count() > 0 )
9719 {
9720 QgsExpressionNode *argNode = node->args()->at( 0 );
9721
9722 if ( argNode->isStatic( parent, context ) )
9723 {
9724 QString expString = argNode->eval( parent, context ).toString();
9725
9726 QgsExpression e( expString );
9727
9728 if ( e.rootNode() && e.rootNode()->isStatic( parent, context ) )
9729 return true;
9730 }
9731 }
9732
9733 return false;
9734 } );
9735
9736 functions << evalFunc;
9737
9738 QgsStaticExpressionFunction *attributeFunc = new QgsStaticExpressionFunction( QStringLiteral( "attribute" ), -1, fcnAttribute, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
9739 attributeFunc->setIsStaticFunction(
9740 []( const QgsExpressionNodeFunction * node, QgsExpression * parent, const QgsExpressionContext * context )
9741 {
9742 const QList< QgsExpressionNode *> argList = node->args()->list();
9743 for ( QgsExpressionNode *argNode : argList )
9744 {
9745 if ( !argNode->isStatic( parent, context ) )
9746 return false;
9747 }
9748
9749 if ( node->args()->count() == 1 )
9750 {
9751 // not static -- this is the variant which uses the current feature taken direct from the expression context
9752 return false;
9753 }
9754
9755 return true;
9756 } );
9757 functions << attributeFunc;
9758
9759 functions
9760 << new QgsStaticExpressionFunction( QStringLiteral( "env" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), fcnEnvVar, QStringLiteral( "General" ), QString() )
9761 << new QgsWithVariableExpressionFunction()
9762 << new QgsStaticExpressionFunction( QStringLiteral( "raster_value" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterValue, QStringLiteral( "Rasters" ) )
9763 << new QgsStaticExpressionFunction( QStringLiteral( "raster_attributes" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "layer" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "band" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "point" ) ), fcnRasterAttributes, QStringLiteral( "Rasters" ) )
9764
9765 // functions for arrays
9766 << new QgsArrayForeachExpressionFunction()
9767 << new QgsArrayFilterExpressionFunction()
9768 << new QgsStaticExpressionFunction( QStringLiteral( "array" ), -1, fcnArray, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9769 << new QgsStaticExpressionFunction( QStringLiteral( "array_sort" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "ascending" ), true, true ), fcnArraySort, QStringLiteral( "Arrays" ) )
9770 << new QgsStaticExpressionFunction( QStringLiteral( "array_length" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLength, QStringLiteral( "Arrays" ) )
9771 << new QgsStaticExpressionFunction( QStringLiteral( "array_contains" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayContains, QStringLiteral( "Arrays" ) )
9772 << new QgsStaticExpressionFunction( QStringLiteral( "array_count" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayCount, QStringLiteral( "Arrays" ) )
9773 << new QgsStaticExpressionFunction( QStringLiteral( "array_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array_a" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_b" ) ), fcnArrayAll, QStringLiteral( "Arrays" ) )
9774 << new QgsStaticExpressionFunction( QStringLiteral( "array_find" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayFind, QStringLiteral( "Arrays" ) )
9775 << new QgsStaticExpressionFunction( QStringLiteral( "array_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayGet, QStringLiteral( "Arrays" ) )
9776 << new QgsStaticExpressionFunction( QStringLiteral( "array_first" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayFirst, QStringLiteral( "Arrays" ) )
9777 << new QgsStaticExpressionFunction( QStringLiteral( "array_last" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayLast, QStringLiteral( "Arrays" ) )
9778 << new QgsStaticExpressionFunction( QStringLiteral( "array_min" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMinimum, QStringLiteral( "Arrays" ) )
9779 << new QgsStaticExpressionFunction( QStringLiteral( "array_max" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMaximum, QStringLiteral( "Arrays" ) )
9780 << new QgsStaticExpressionFunction( QStringLiteral( "array_mean" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMean, QStringLiteral( "Arrays" ) )
9781 << new QgsStaticExpressionFunction( QStringLiteral( "array_median" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayMedian, QStringLiteral( "Arrays" ) )
9782 << new QgsStaticExpressionFunction( QStringLiteral( "array_majority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMajority, QStringLiteral( "Arrays" ) )
9783 << new QgsStaticExpressionFunction( QStringLiteral( "array_minority" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, QVariant( "all" ) ), fcnArrayMinority, QStringLiteral( "Arrays" ) )
9784 << new QgsStaticExpressionFunction( QStringLiteral( "array_sum" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArraySum, QStringLiteral( "Arrays" ) )
9785 << new QgsStaticExpressionFunction( QStringLiteral( "array_append" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayAppend, QStringLiteral( "Arrays" ) )
9786 << new QgsStaticExpressionFunction( QStringLiteral( "array_prepend" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayPrepend, QStringLiteral( "Arrays" ) )
9787 << new QgsStaticExpressionFunction( QStringLiteral( "array_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayInsert, QStringLiteral( "Arrays" ) )
9788 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_at" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "pos" ) ), fcnArrayRemoveAt, QStringLiteral( "Arrays" ) )
9789 << new QgsStaticExpressionFunction( QStringLiteral( "array_remove_all" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnArrayRemoveAll, QStringLiteral( "Arrays" ), QString(), false, QSet<QString>(), false, QStringList(), true )
9790 << new QgsStaticExpressionFunction( QStringLiteral( "array_replace" ), -1, fcnArrayReplace, QStringLiteral( "Arrays" ) )
9791 << new QgsStaticExpressionFunction( QStringLiteral( "array_prioritize" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array_prioritize" ) ), fcnArrayPrioritize, QStringLiteral( "Arrays" ) )
9792 << new QgsStaticExpressionFunction( QStringLiteral( "array_cat" ), -1, fcnArrayCat, QStringLiteral( "Arrays" ) )
9793 << new QgsStaticExpressionFunction( QStringLiteral( "array_slice" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "start_pos" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "end_pos" ) ), fcnArraySlice, QStringLiteral( "Arrays" ) )
9794 << new QgsStaticExpressionFunction( QStringLiteral( "array_reverse" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayReverse, QStringLiteral( "Arrays" ) )
9795 << new QgsStaticExpressionFunction( QStringLiteral( "array_intersect" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "array2" ) ), fcnArrayIntersect, QStringLiteral( "Arrays" ) )
9796 << new QgsStaticExpressionFunction( QStringLiteral( "array_distinct" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) ), fcnArrayDistinct, QStringLiteral( "Arrays" ) )
9797 << 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" ) )
9798 << 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" ) )
9799 << 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" ) )
9800 << new QgsStaticExpressionFunction( QStringLiteral( "geometries_to_array" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometries" ) ), fcnGeometryCollectionAsArray, QStringLiteral( "Arrays" ) )
9801
9802 //functions for maps
9803 << new QgsStaticExpressionFunction( QStringLiteral( "from_json" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnLoadJson, QStringLiteral( "Maps" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "json_to_map" ) )
9804 << 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" ) )
9805 << new QgsStaticExpressionFunction( QStringLiteral( "hstore_to_map" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ), fcnHstoreToMap, QStringLiteral( "Maps" ) )
9806 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_hstore" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapToHstore, QStringLiteral( "Maps" ) )
9807 << new QgsStaticExpressionFunction( QStringLiteral( "map" ), -1, fcnMap, QStringLiteral( "Maps" ) )
9808 << new QgsStaticExpressionFunction( QStringLiteral( "map_get" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapGet, QStringLiteral( "Maps" ) )
9809 << new QgsStaticExpressionFunction( QStringLiteral( "map_exist" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapExist, QStringLiteral( "Maps" ) )
9810 << new QgsStaticExpressionFunction( QStringLiteral( "map_delete" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ), fcnMapDelete, QStringLiteral( "Maps" ) )
9811 << new QgsStaticExpressionFunction( QStringLiteral( "map_insert" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "key" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) ), fcnMapInsert, QStringLiteral( "Maps" ) )
9812 << new QgsStaticExpressionFunction( QStringLiteral( "map_concat" ), -1, fcnMapConcat, QStringLiteral( "Maps" ) )
9813 << new QgsStaticExpressionFunction( QStringLiteral( "map_akeys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAKeys, QStringLiteral( "Maps" ) )
9814 << new QgsStaticExpressionFunction( QStringLiteral( "map_avals" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ), fcnMapAVals, QStringLiteral( "Maps" ) )
9815 << new QgsStaticExpressionFunction( QStringLiteral( "map_prefix_keys" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) )
9816 << QgsExpressionFunction::Parameter( QStringLiteral( "prefix" ) ),
9817 fcnMapPrefixKeys, QStringLiteral( "Maps" ) )
9818 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_table" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9819 fcnMapToHtmlTable, QStringLiteral( "Maps" ) )
9820 << new QgsStaticExpressionFunction( QStringLiteral( "map_to_html_dl" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9821 fcnMapToHtmlDefinitionList, QStringLiteral( "Maps" ) )
9822 << new QgsStaticExpressionFunction( QStringLiteral( "url_encode" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "map" ) ),
9823 fcnToFormUrlEncode, QStringLiteral( "Maps" ) )
9824
9825 ;
9826
9828
9829 //QgsExpression has ownership of all built-in functions
9830 for ( QgsExpressionFunction *func : std::as_const( functions ) )
9831 {
9832 *sOwnedFunctions() << func;
9833 *sBuiltinFunctions() << func->name();
9834 sBuiltinFunctions()->append( func->aliases() );
9835 }
9836 }
9837 return functions;
9838}
9839
9840bool QgsExpression::registerFunction( QgsExpressionFunction *function, bool transferOwnership )
9841{
9842 int fnIdx = functionIndex( function->name() );
9843 if ( fnIdx != -1 )
9844 {
9845 return false;
9846 }
9847
9848 QMutexLocker locker( &sFunctionsMutex );
9849 sFunctions()->append( function );
9850 if ( transferOwnership )
9851 sOwnedFunctions()->append( function );
9852
9853 return true;
9854}
9855
9856bool QgsExpression::unregisterFunction( const QString &name )
9857{
9858 // You can never override the built in functions.
9859 if ( QgsExpression::BuiltinFunctions().contains( name ) )
9860 {
9861 return false;
9862 }
9863 int fnIdx = functionIndex( name );
9864 if ( fnIdx != -1 )
9865 {
9866 QMutexLocker locker( &sFunctionsMutex );
9867 sFunctions()->removeAt( fnIdx );
9868 sFunctionIndexMap.clear();
9869 return true;
9870 }
9871 return false;
9872}
9873
9875{
9876 qDeleteAll( *sOwnedFunctions() );
9877 sOwnedFunctions()->clear();
9879
9880const QStringList &QgsExpression::BuiltinFunctions()
9881{
9882 if ( sBuiltinFunctions()->isEmpty() )
9883 {
9884 Functions(); // this method builds the gmBuiltinFunctions as well
9885 }
9886 return *sBuiltinFunctions();
9888
9890 : QgsExpressionFunction( QStringLiteral( "array_foreach" ), QgsExpressionFunction::ParameterList() // skip-keyword-check
9891 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9892 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
9893 QStringLiteral( "Arrays" ) )
9894{
9895
9896}
9897
9899{
9900 bool isStatic = false;
9901
9902 QgsExpressionNode::NodeList *args = node->args();
9904 if ( args->count() < 2 )
9905 return false;
9906
9907 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
9908 {
9909 isStatic = true;
9910 }
9911 return isStatic;
9912}
9913
9915{
9916 Q_UNUSED( node )
9917 QVariantList result;
9918
9919 if ( args->count() < 2 )
9920 // error
9921 return result;
9922
9923 QVariantList array = args->at( 0 )->eval( parent, context ).toList();
9924
9925 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
9926 std::unique_ptr< QgsExpressionContext > tempContext;
9927 if ( !subContext )
9928 {
9929 tempContext = std::make_unique< QgsExpressionContext >();
9930 subContext = tempContext.get();
9931 }
9932
9933 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
9934 subContext->appendScope( subScope );
9935
9936 int i = 0;
9937 for ( QVariantList::const_iterator it = array.constBegin(); it != array.constEnd(); ++it, ++i )
9938 {
9939 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), *it, true ) );
9940 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), i, true ) );
9941 result << args->at( 1 )->eval( parent, subContext );
9942 }
9943
9944 if ( context )
9945 delete subContext->popScope();
9946
9947 return result;
9948}
9949
9950QVariant QgsArrayForeachExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
9952 // This is a dummy function, all the real handling is in run
9953 Q_UNUSED( values )
9954 Q_UNUSED( context )
9955 Q_UNUSED( parent )
9956 Q_UNUSED( node )
9957
9958 Q_ASSERT( false );
9959 return QVariant();
9960}
9961
9963{
9964 QgsExpressionNode::NodeList *args = node->args();
9965
9966 if ( args->count() < 2 )
9967 // error
9968 return false;
9969
9970 args->at( 0 )->prepare( parent, context );
9971
9972 QgsExpressionContext subContext;
9973 if ( context )
9974 subContext = *context;
9977 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
9978 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "counter" ), QVariant(), true ) );
9979 subContext.appendScope( subScope );
9980
9981 args->at( 1 )->prepare( parent, &subContext );
9982
9983 return true;
9984}
9987 : QgsExpressionFunction( QStringLiteral( "array_filter" ), QgsExpressionFunction::ParameterList()
9988 << QgsExpressionFunction::Parameter( QStringLiteral( "array" ) )
9989 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) )
9990 << QgsExpressionFunction::Parameter( QStringLiteral( "limit" ), true, 0 ),
9991 QStringLiteral( "Arrays" ) )
9992{
9993
9994}
9995
9997{
9998 bool isStatic = false;
9999
10000 QgsExpressionNode::NodeList *args = node->args();
10002 if ( args->count() < 2 )
10003 return false;
10004
10005 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10006 {
10007 isStatic = true;
10008 }
10009 return isStatic;
10010}
10011
10013{
10014 Q_UNUSED( node )
10015 QVariantList result;
10016
10017 if ( args->count() < 2 )
10018 // error
10019 return result;
10020
10021 const QVariantList array = args->at( 0 )->eval( parent, context ).toList();
10022
10023 QgsExpressionContext *subContext = const_cast<QgsExpressionContext *>( context );
10024 std::unique_ptr< QgsExpressionContext > tempContext;
10025 if ( !subContext )
10026 {
10027 tempContext = std::make_unique< QgsExpressionContext >();
10028 subContext = tempContext.get();
10029 }
10030
10031 QgsExpressionContextScope *subScope = new QgsExpressionContextScope();
10032 subContext->appendScope( subScope );
10033
10034 int limit = 0;
10035 if ( args->count() >= 3 )
10036 {
10037 const QVariant limitVar = args->at( 2 )->eval( parent, context );
10038
10039 if ( QgsExpressionUtils::isIntSafe( limitVar ) )
10040 {
10041 limit = limitVar.toInt();
10042 }
10043 else
10044 {
10045 return result;
10046 }
10047 }
10048
10049 for ( const QVariant &value : array )
10050 {
10051 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), value, true ) );
10052 if ( args->at( 1 )->eval( parent, subContext ).toBool() )
10053 {
10054 result << value;
10055
10056 if ( limit > 0 && limit == result.size() )
10057 break;
10058 }
10059 }
10060
10061 if ( context )
10062 delete subContext->popScope();
10063
10064 return result;
10065}
10066
10067QVariant QgsArrayFilterExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10069 // This is a dummy function, all the real handling is in run
10070 Q_UNUSED( values )
10071 Q_UNUSED( context )
10072 Q_UNUSED( parent )
10073 Q_UNUSED( node )
10074
10075 Q_ASSERT( false );
10076 return QVariant();
10077}
10078
10080{
10081 QgsExpressionNode::NodeList *args = node->args();
10082
10083 if ( args->count() < 2 )
10084 // error
10085 return false;
10086
10087 args->at( 0 )->prepare( parent, context );
10088
10089 QgsExpressionContext subContext;
10090 if ( context )
10091 subContext = *context;
10092
10094 subScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "element" ), QVariant(), true ) );
10095 subContext.appendScope( subScope );
10096
10097 args->at( 1 )->prepare( parent, &subContext );
10098
10099 return true;
10102 : QgsExpressionFunction( QStringLiteral( "with_variable" ), QgsExpressionFunction::ParameterList() <<
10103 QgsExpressionFunction::Parameter( QStringLiteral( "name" ) )
10104 << QgsExpressionFunction::Parameter( QStringLiteral( "value" ) )
10105 << QgsExpressionFunction::Parameter( QStringLiteral( "expression" ) ),
10106 QStringLiteral( "General" ) )
10107{
10108
10109}
10110
10112{
10113 bool isStatic = false;
10114
10115 QgsExpressionNode::NodeList *args = node->args();
10116
10117 if ( args->count() < 3 )
10118 return false;
10119
10120 // We only need to check if the node evaluation is static, if both - name and value - are static.
10121 if ( args->at( 0 )->isStatic( parent, context ) && args->at( 1 )->isStatic( parent, context ) )
10122 {
10123 QVariant name = args->at( 0 )->eval( parent, context );
10124 QVariant value = args->at( 1 )->eval( parent, context );
10126 // Temporarily append a new scope to provide the variable
10127 appendTemporaryVariable( context, name.toString(), value );
10128 if ( args->at( 2 )->isStatic( parent, context ) )
10129 isStatic = true;
10130 popTemporaryVariable( context );
10131 }
10132
10133 return isStatic;
10134}
10135
10137{
10138 Q_UNUSED( node )
10139 QVariant result;
10140
10141 if ( args->count() < 3 )
10142 // error
10143 return result;
10144
10145 QVariant name = args->at( 0 )->eval( parent, context );
10146 QVariant value = args->at( 1 )->eval( parent, context );
10147
10148 const QgsExpressionContext *updatedContext = context;
10149 std::unique_ptr< QgsExpressionContext > tempContext;
10150 if ( !updatedContext )
10151 {
10152 tempContext = std::make_unique< QgsExpressionContext >();
10153 updatedContext = tempContext.get();
10155
10156 appendTemporaryVariable( updatedContext, name.toString(), value );
10157 result = args->at( 2 )->eval( parent, updatedContext );
10158
10159 if ( context )
10160 popTemporaryVariable( updatedContext );
10161
10162 return result;
10163}
10164
10165QVariant QgsWithVariableExpressionFunction::func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node )
10167 // This is a dummy function, all the real handling is in run
10168 Q_UNUSED( values )
10169 Q_UNUSED( context )
10170 Q_UNUSED( parent )
10171 Q_UNUSED( node )
10172
10173 Q_ASSERT( false );
10174 return QVariant();
10175}
10176
10178{
10179 QgsExpressionNode::NodeList *args = node->args();
10180
10181 if ( args->count() < 3 )
10182 // error
10183 return false;
10184
10185 QVariant name = args->at( 0 )->prepare( parent, context );
10186 QVariant value = args->at( 1 )->prepare( parent, context );
10187
10188 const QgsExpressionContext *updatedContext = context;
10189 std::unique_ptr< QgsExpressionContext > tempContext;
10190 if ( !updatedContext )
10191 {
10192 tempContext = std::make_unique< QgsExpressionContext >();
10193 updatedContext = tempContext.get();
10194 }
10195
10196 appendTemporaryVariable( updatedContext, name.toString(), value );
10197 args->at( 2 )->prepare( parent, updatedContext );
10198
10199 if ( context )
10200 popTemporaryVariable( updatedContext );
10201
10202 return true;
10203}
10204
10205void QgsWithVariableExpressionFunction::popTemporaryVariable( const QgsExpressionContext *context ) const
10206{
10207 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10208 delete updatedContext->popScope();
10209}
10210
10211void QgsWithVariableExpressionFunction::appendTemporaryVariable( const QgsExpressionContext *context, const QString &name, const QVariant &value ) const
10212{
10213 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
10214 scope->addVariable( QgsExpressionContextScope::StaticVariable( name, value, true ) );
10215
10216 QgsExpressionContext *updatedContext = const_cast<QgsExpressionContext *>( context );
10217 updatedContext->appendScope( scope );
10218}
@ Left
Buffer to left of line.
Definition qgis.h:2097
DashPatternSizeAdjustment
Dash pattern size adjustment options.
Definition qgis.h:3291
@ ScaleDashOnly
Only dash lengths are adjusted.
Definition qgis.h:3293
@ ScaleBothDashAndGap
Both the dash and gap lengths are adjusted equally.
Definition qgis.h:3292
@ ScaleGapOnly
Only gap lengths are adjusted.
Definition qgis.h:3294
@ Success
Operation succeeded.
Definition qgis.h:2043
@ Visvalingam
The simplification gives each point in a line an importance weighting, so that least important points...
Definition qgis.h:3016
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2196
@ Point
Points.
Definition qgis.h:359
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
@ Unknown
Unknown types.
Definition qgis.h:362
@ Null
No geometry.
Definition qgis.h:363
JoinStyle
Join styles for buffers.
Definition qgis.h:2121
@ Bevel
Use beveled joins.
Definition qgis.h:2124
@ Round
Use rounded joins.
Definition qgis.h:2122
@ Miter
Use mitered joins.
Definition qgis.h:2123
RasterBandStatistic
Available raster band statistics.
Definition qgis.h:5939
@ StdDev
Standard deviation.
Definition qgis.h:5946
@ NoStatistic
No statistic.
Definition qgis.h:5940
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:198
@ Plugin
Plugin based layer.
Definition qgis.h:193
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:199
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:196
@ Vector
Vector layer.
Definition qgis.h:191
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:195
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:194
@ Raster
Raster layer.
Definition qgis.h:192
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:197
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2108
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2110
@ Round
Round cap.
Definition qgis.h:2109
@ Square
Square cap (extends past start/end of line by buffer distance).
Definition qgis.h:2111
Aggregate
Available aggregates to calculate.
Definition qgis.h:5816
@ StringMinimumLength
Minimum length of string (string fields only).
Definition qgis.h:5833
@ FirstQuartile
First quartile (numeric fields only).
Definition qgis.h:5830
@ Mean
Mean of values (numeric fields only).
Definition qgis.h:5823
@ Median
Median of values (numeric fields only).
Definition qgis.h:5824
@ Max
Max of values.
Definition qgis.h:5821
@ Min
Min of values.
Definition qgis.h:5820
@ StringMaximumLength
Maximum length of string (string fields only).
Definition qgis.h:5834
@ Range
Range of values (max - min) (numeric and datetime fields only).
Definition qgis.h:5827
@ StringConcatenateUnique
Concatenate unique values with a joining string (string fields only). Specify the delimiter using set...
Definition qgis.h:5838
@ Sum
Sum of values.
Definition qgis.h:5822
@ Minority
Minority of values.
Definition qgis.h:5828
@ CountMissing
Number of missing (null) values.
Definition qgis.h:5819
@ ArrayAggregate
Create an array of values.
Definition qgis.h:5837
@ Majority
Majority of values.
Definition qgis.h:5829
@ StDevSample
Sample standard deviation of values (numeric fields only).
Definition qgis.h:5826
@ Count
Count.
Definition qgis.h:5817
@ ThirdQuartile
Third quartile (numeric fields only).
Definition qgis.h:5831
@ CountDistinct
Number of distinct values.
Definition qgis.h:5818
@ StringConcatenate
Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimit...
Definition qgis.h:5835
@ GeometryCollect
Create a multipart geometry from aggregated geometries.
Definition qgis.h:5836
@ InterQuartileRange
Inter quartile range (IQR) (numeric fields only).
Definition qgis.h:5832
DashPatternLineEndingRule
Dash pattern line ending rules.
Definition qgis.h:3276
@ HalfDash
Start or finish the pattern with a half length dash.
Definition qgis.h:3279
@ HalfGap
Start or finish the pattern with a half length gap.
Definition qgis.h:3281
@ FullGap
Start or finish the pattern with a full gap.
Definition qgis.h:3280
@ FullDash
Start or finish the pattern with a full dash.
Definition qgis.h:3278
@ NoRule
No special rule.
Definition qgis.h:3277
MakeValidMethod
Algorithms to use when repairing invalid geometries.
Definition qgis.h:2180
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
Definition qgis.h:2181
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
Definition qgis.h:2182
@ Point
Point.
Definition qgis.h:279
@ PointM
PointM.
Definition qgis.h:310
@ PointZ
PointZ.
Definition qgis.h:295
@ GeometryCollection
GeometryCollection.
Definition qgis.h:286
@ PointZM
PointZM.
Definition qgis.h:325
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...
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
bool isMeasure() const
Returns true if the geometry contains m values.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
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.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
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.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
static Qgis::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.
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...
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.
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.
QFlags< FormatFlag > FormatFlags
@ 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.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
bool isEmpty() const override
Returns true if the geometry is empty.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
double area() const override
Returns the planar, 2-dimensional area of the geometry.
double roundness() const
Returns the roundness of the curve polygon.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
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:278
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition qgscurve.cpp:176
virtual QgsCurve * curveSubstring(double startDistance, double endDistance) const =0
Returns a new curve representing a substring of this curve.
virtual bool isClosed() const
Returns true if the curve is closed.
Definition qgscurve.cpp:54
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition qgscurve.cpp:273
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 convertLengthMeasurement(double length, Qgis::DistanceUnit toUnits) const
Takes a length measurement calculated by this QgsDistanceArea object and converts it to a different d...
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 bearing(const QgsPointXY &p1, const QgsPointXY &p2) const
Computes the bearing (in radians) between two points.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
double convertAreaMeasurement(double area, Qgis::AreaUnit toUnits) const
Takes an area measurement calculated by this QgsDistanceArea object and converts it to a different ar...
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
Ellipse geometry type.
Definition qgsellipse.h:39
QString what() const
Contains utilities for working with EXIF tags in images.
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.
QString uniqueHash(bool &ok, const QSet< QString > &variables=QSet< QString >()) const
Returns a unique hash representing the current state of the 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.
An abstract base class for defining QgsExpression functions.
QList< QgsExpressionFunction::Parameter > ParameterList
List of parameters, used for function definition.
bool operator==(const QgsExpressionFunction &other) const
QgsExpressionFunction(const QString &fnname, int params, const QString &group, const QString &helpText=QString(), bool lazyEval=false, bool handlesNull=false, bool isContextual=false)
Constructor for function which uses unnamed parameters.
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.
const QString helpText() const
The help text for the function.
virtual bool usesGeometry(const QgsExpressionNodeFunction *node) const
Does this function use a geometry object.
An expression node which takes its 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.
Handles 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()
QString expression() const
Returns the original, unmodified expression string.
static void cleanRegisteredFunctions()
Deletes all registered functions whose ownership have been transferred to the expression engine.
Qgis::DistanceUnit distanceUnits() const
Returns the desired distance units for calculations involving geomCalculator(), e....
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()
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
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...
static PRIVATE QString helpText(QString name)
Returns the help text for a specified function.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static bool unregisterFunction(const QString &name)
Unregisters a function from the expression engine.
Qgis::AreaUnit areaUnits() const
Returns the desired areal units for calculations involving geomCalculator(), e.g.,...
void setEvalErrorString(const QString &str)
Sets evaluation error (used internally by evaluation functions).
friend class QgsExpressionNodeFunction
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsExpression(const QString &expr)
Creates a new expression based on the provided string.
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)
Fetch next feature and stores in f, returns true on success.
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.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
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 & 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 & 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...
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
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:58
QgsFields fields
Definition qgsfeature.h:68
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
ConstraintStrength
Strength of constraints.
@ ConstraintStrengthNotSet
Constraint is not set.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
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:63
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:747
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
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.
int numGeometries() const
Returns the number of geometries within the collection.
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.
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...
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double lineLocatePoint(const QgsGeometry &point) const
Returns a distance representing the location along this linestring of the closest point on this lines...
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 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.
QgsGeometry mergeLines(const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
QgsGeometry makeValid(Qgis::MakeValidMethod method=Qgis::MakeValidMethod::Linework, bool keepCollapsed=false) const
Attempts to make an invalid geometry valid without losing vertices.
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
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 ...
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
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.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
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.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsGeometry fromWkt(const QString &wkt)
Creates a new geometry from a WKT string.
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.
Qgis::GeometryType type
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.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
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.
double hausdorffDistance(const QgsGeometry &geom) const
Returns the Hausdorff distance between this geometry and geom.
QgsGeometry concaveHull(double targetPercent, bool allowHoles=false) const
Returns a possibly concave polygon that contains all the points in the geometry.
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.
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygonXY.
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...
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...
Q_INVOKABLE QString asWkt(int precision=17) const
Exports the geometry to WKT.
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.).
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
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, and exception handling.
Definition qgsgeos.h:141
std::unique_ptr< QgsAbstractGeometry > maximumInscribedCircle(double tolerance, QString *errorMsg=nullptr) const
Returns the maximum inscribed circle.
Definition qgsgeos.cpp:2811
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Represents a color stop within a QgsGradientColorRamp color ramp.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
A representation of the interval between two datetime values.
Definition qgsinterval.h:47
bool isValid() const
Returns true if the interval is valid.
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.
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.
bool lineLocatePointByM(double m, double &x, double &y, double &z, double &distanceFromStart, bool use3DDistance=true) const
Attempts to locate a point on the linestring by m value.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Base class for all map layer types.
Definition qgsmaplayer.h:80
QString name
Definition qgsmaplayer.h:84
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.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
QString id
Definition qgsmaplayer.h:83
QgsLayerMetadata metadata
Definition qgsmaplayer.h:86
Qgis::LayerType type
Definition qgsmaplayer.h:90
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
virtual bool isEditable() const
Returns true if the layer can be edited.
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 mapTipTemplate
Definition qgsmaplayer.h:93
Implementation of a geometry simplifier using the "MapToPixel" algorithm.
@ 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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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.
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.
static QgsGeometry geometryFromGML(const QString &xmlString, const QgsOgcUtils::Context &context=QgsOgcUtils::Context())
Static method that creates geometry from GML.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double inclination(const QgsPoint &other) const
Calculates Cartesian inclination between this point and other one (starting from zenith = 0 to nadir ...
Definition qgspoint.cpp:695
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition qgspoint.cpp:561
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition qgspoint.cpp:429
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition qgspoint.cpp:108
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
double m
Definition qgspoint.h:55
QgsPoint project(double distance, double azimuth, double inclination=90.0) const
Returns a new point which corresponds to this point projected by a specified distance with specified ...
Definition qgspoint.cpp:707
double y
Definition qgspoint.h:53
QgsRelationManager * relationManager
Definition qgsproject.h:120
static QgsProject * instance()
Returns the QgsProject singleton instance.
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)
Construct a QgsQuadrilateral as a square from a diagonal.
QgsPolygon * toPolygon(bool force2D=false) const
Returns the quadrilateral as a new polygon.
static QgsQuadrilateral rectangleFrom3Points(const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p3, ConstructionOption mode)
Construct a QgsQuadrilateral as a Rectangle from 3 points.
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...
The Field class represents a Raster Attribute Table field, including its name, usage and type.
bool isRamp() const
Returns true if the field carries a color ramp component information (RedMin/RedMax,...
bool isColor() const
Returns true if the field carries a color component (Red, Green, Blue and optionally Alpha) informati...
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.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
void grow(double delta)
Grows the rectangle in place by the specified amount.
double yMaximum
QgsPointXY center
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.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
Q_INVOKABLE 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.
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.
void setUsesGeometryFunction(const std::function< bool(const QgsExpressionNodeFunction *node)> &usesGeometry)
Set a function that will be called when determining if the function requires feature geometry 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:508
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:147
Contains utility functions for working with symbols and symbol layers.
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.
Allows creation of a multi-layer database-side transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QVariant aggregate(Qgis::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.
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
QgsEditorWidgetSetup editorWidgetSetup(int index) const
Returns the editor widget setup for the field at the specified index.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
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 Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE QString geometryDisplayString(Qgis::GeometryType type)
Returns a display string for a geometry type.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
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).
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).
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
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:588
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169
double qgsRound(double number, int places)
Returns a double number, rounded (as close as possible) to the specified number of places.
Definition qgis.h:6648
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
T qgsgeometry_cast(QgsAbstractGeometry *geom)
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
QVariant fcnRampColor(const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node)
#define ENSURE_GEOM_TYPE(f, g, geomtype)
QVariant fcnRampColorObject(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
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
Definition qgsgeometry.h:96
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
QLineF segment(int index, QRectF rect, double radius)
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:64
const QgsMapLayer * layer
Definition qgsogcutils.h:74
QgsCoordinateTransformContext transformContext
Definition qgsogcutils.h:75
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30