Skip to content

Commit

Permalink
[processing] Support specifying data defined parameter values
Browse files Browse the repository at this point in the history
using --PARAM=field:FIELD_NAME  or --PARAM=expression:SOME EXPRESSION
when using qgis_process utility

Fixes #50482
  • Loading branch information
nyalldawson committed Jan 16, 2023
1 parent 2de0d03 commit 91886a2
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 11 deletions.
6 changes: 2 additions & 4 deletions src/core/processing/qgsprocessingparameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2692,12 +2692,10 @@ QString QgsProcessingParameterDefinition::valueAsStringPrivate( const QVariant &
return QString();
case QgsProperty::StaticProperty:
return valueAsString( prop.staticValue(), context, ok );

// these are not supported for serialization
case QgsProperty::FieldBasedProperty:
return QStringLiteral( "field:%1" ).arg( prop.field() );
case QgsProperty::ExpressionBasedProperty:
QgsDebugMsg( QStringLiteral( "could not convert expression/field based property to string" ) );
return QString();
return QStringLiteral( "expression:%1" ).arg( prop.expressionString() );
}
}

Expand Down
28 changes: 21 additions & 7 deletions src/core/processing/qgsprocessingparametertypeimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,9 @@ class CORE_EXPORT QgsProcessingParameterTypeBoolean : public QgsProcessingParame
QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "1 for true/yes" )
<< QObject::tr( "0 for false/no" );
<< QObject::tr( "0 for false/no" )
<< QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" )
<< QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" );
}
};

Expand Down Expand Up @@ -1124,7 +1126,9 @@ class CORE_EXPORT QgsProcessingParameterTypeString : public QgsProcessingParamet

QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "String value" );
return QStringList() << QObject::tr( "String value" )
<< QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" )
<< QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" );
}
};

Expand Down Expand Up @@ -1330,7 +1334,9 @@ class CORE_EXPORT QgsProcessingParameterTypeNumber : public QgsProcessingParamet

QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "A numeric value" );
return QStringList() << QObject::tr( "A numeric value" )
<< QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" )
<< QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" );
}
};

Expand Down Expand Up @@ -1382,7 +1388,9 @@ class CORE_EXPORT QgsProcessingParameterTypeDistance : public QgsProcessingParam

QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "A numeric value" );
return QStringList() << QObject::tr( "A numeric value" )
<< QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" )
<< QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" );
}
};

Expand Down Expand Up @@ -1434,7 +1442,9 @@ class CORE_EXPORT QgsProcessingParameterTypeDuration : public QgsProcessingParam

QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "A numeric value (unit type set by algorithms)" );
return QStringList() << QObject::tr( "A numeric value (unit type set by algorithms)" )
<< QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" )
<< QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" );
}
};

Expand Down Expand Up @@ -1753,7 +1763,9 @@ class CORE_EXPORT QgsProcessingParameterTypeColor : public QgsProcessingParamete

QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "String representation of color, e.g #ff0000 or rgba(200,100,50,0.8)" );
return QStringList() << QObject::tr( "String representation of color, e.g #ff0000 or rgba(200,100,50,0.8)" )
<< QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" )
<< QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" );
}
};

Expand Down Expand Up @@ -1908,7 +1920,9 @@ class CORE_EXPORT QgsProcessingParameterTypeDateTime : public QgsProcessingParam

QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "A datetime value in ISO format" );
return QStringList() << QObject::tr( "A datetime value in ISO format" )
<< QObject::tr( "field:FIELD_NAME to use a data defined value taken from the FIELD_NAME field" )
<< QObject::tr( "expression:SOME EXPRESSION to use a data defined value calculated using a custom QGIS expression" );
}
};

Expand Down
17 changes: 17 additions & 0 deletions src/core/processing/qgsprocessingutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1411,6 +1411,23 @@ QVariantMap QgsProcessingUtils::preprocessQgisProcessParameters( const QVariantM
output.insert( it.key(), it.value() );
}
}
else if ( it.value().type() == QVariant::String )
{
const QString stringValue = it.value().toString();

if ( stringValue.startsWith( QLatin1String( "field:" ) ) )
{
output.insert( it.key(), QgsProperty::fromField( stringValue.mid( 6 ) ) );
}
else if ( stringValue.startsWith( QLatin1String( "expression:" ) ) )
{
output.insert( it.key(), QgsProperty::fromExpression( stringValue.mid( 11 ) ) );
}
else
{
output.insert( it.key(), it.value() );
}
}
else
{
output.insert( it.key(), it.value() );
Expand Down
14 changes: 14 additions & 0 deletions tests/src/analysis/testqgsprocessing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,14 @@ class DummyAlgorithm : public QgsProcessingAlgorithm
params.insert( "p2", QStringLiteral( "thisisa'test" ) );
QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --distance_units=meters --p1=a --p2='thisisa'\\''test'" ) );
QVERIFY( ok );

addParameter( new QgsProcessingParameterString( "dd_field", QString(), true ) );
addParameter( new QgsProcessingParameterString( "dd_expression", QString(), true ) );

params.insert( "dd_field", QgsProperty::fromField( QStringLiteral( "my field" ) ) );
params.insert( "dd_expression", QgsProperty::fromExpression( QStringLiteral( "\"some field\" * 200" ) ) );
QCOMPARE( asQgisProcessCommand( params, context, ok ), QStringLiteral( "qgis_process run test --distance_units=meters --p1=a --p2='thisisa'\\''test' --dd_field='field:my field' --dd_expression='expression:\"some field\" * 200'" ) );
QVERIFY( ok );
}

void runAsAsJsonMapChecks()
Expand Down Expand Up @@ -11687,6 +11695,8 @@ void TestQgsProcessing::preprocessParameters()
inputs.insert( QStringLiteral( "string" ), QStringLiteral( "a string" ) );
inputs.insert( QStringLiteral( "data defined field" ), QVariantMap( {{QStringLiteral( "type" ), QStringLiteral( "data_defined" )}, {QStringLiteral( "field" ), QStringLiteral( "DEPTH_FIELD" ) }} ) );
inputs.insert( QStringLiteral( "data defined expression" ), QVariantMap( {{QStringLiteral( "type" ), QStringLiteral( "data_defined" )}, {QStringLiteral( "expression" ), QStringLiteral( "A_FIELD * 200" ) }} ) );
inputs.insert( QStringLiteral( "data defined field using string" ), QStringLiteral( "field:MY FIELD" ) );
inputs.insert( QStringLiteral( "data defined expression using string" ), QStringLiteral( "expression:SOME_FIELD * 2" ) );
inputs.insert( QStringLiteral( "invalid" ), QVariantMap( {{QStringLiteral( "type" ), QStringLiteral( "data_defined" )}} ) );

bool ok = false;
Expand All @@ -11705,8 +11715,12 @@ void TestQgsProcessing::preprocessParameters()
QCOMPARE( outputs.value( QStringLiteral( "string" ) ).toString(), QStringLiteral( "a string" ) );
QCOMPARE( outputs.value( QStringLiteral( "data defined field" ) ).value< QgsProperty >().propertyType(), QgsProperty::FieldBasedProperty );
QCOMPARE( outputs.value( QStringLiteral( "data defined field" ) ).value< QgsProperty >().field(), QStringLiteral( "DEPTH_FIELD" ) );
QCOMPARE( outputs.value( QStringLiteral( "data defined field using string" ) ).value< QgsProperty >().propertyType(), QgsProperty::FieldBasedProperty );
QCOMPARE( outputs.value( QStringLiteral( "data defined field using string" ) ).value< QgsProperty >().field(), QStringLiteral( "MY FIELD" ) );
QCOMPARE( outputs.value( QStringLiteral( "data defined expression" ) ).value< QgsProperty >().propertyType(), QgsProperty::ExpressionBasedProperty );
QCOMPARE( outputs.value( QStringLiteral( "data defined expression" ) ).value< QgsProperty >().expressionString(), QStringLiteral( "A_FIELD * 200" ) );
QCOMPARE( outputs.value( QStringLiteral( "data defined expression using string" ) ).value< QgsProperty >().propertyType(), QgsProperty::ExpressionBasedProperty );
QCOMPARE( outputs.value( QStringLiteral( "data defined expression using string" ) ).value< QgsProperty >().expressionString(), QStringLiteral( "SOME_FIELD * 2" ) );

// test round trip of data defined parameters
const QgsProcessingAlgorithm *bufferAlg = QgsApplication::processingRegistry()->algorithmById( "native:buffer" );
Expand Down
13 changes: 13 additions & 0 deletions tests/src/python/test_qgsprocessexecutable_pt2.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,19 @@ def testLoadLayer(self):
self.assertEqual(rc, 1)

def testDynamicParameters(self):
output_file = self.TMP_DIR + '/dynamic_out2.shp'

rc, output, err = self.run_process(
['run', 'native:buffer', '--INPUT=' + TEST_DATA_DIR + '/points.shp', '--OUTPUT=' + output_file, '--DISTANCE=field:fid', '--json'])
self.assertFalse(self._strip_ignorable_errors(err))

self.assertEqual(rc, 0)

res = json.loads(output)
self.assertEqual(res['algorithm_details']['id'], 'native:buffer')
self.assertEqual(res['inputs']['DISTANCE'], 'field:fid')

def testDynamicParametersJson(self):
output_file = self.TMP_DIR + '/dynamic_out.shp'

params = {
Expand Down

0 comments on commit 91886a2

Please sign in to comment.