Skip to content

Commit

Permalink
Add affine transform expression (#44771)
Browse files Browse the repository at this point in the history
  • Loading branch information
Koyaani committed Sep 2, 2021
1 parent 2c24e88 commit 9e38e43
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
23 changes: 23 additions & 0 deletions resources/function_help/json/affine_transform
@@ -0,0 +1,23 @@
{
"name": "affine_transform",
"type": "function",
"groups": ["GeometryGroup"],
"description": "Returns the geometry after an affine transformation. Calculations are in the Spatial Reference System of this geometry. The operations are performed in a scale, rotation, translation order. If there is a Z or M offset but the coordinate is not present in the geometry, it will be added.",
"arguments": [
{"arg":"geometry","description":"a geometry"},
{"arg":"deltaX","description":"x-axis translation"},
{"arg":"deltaY","description":"y-axis translation"},
{"arg":"rotationZ","description":"rotation around z-axis in degrees counter-clockwise"},
{"arg":"scaleX","description":"x-axis scale factor"},
{"arg":"scaleY","description":"y-axis scale factor"},
{"arg":"deltaZ","optional":true,"default":"0","description":"z-axis translation"},
{"arg":"deltaM","optional":true,"default":"0","description":"m-axis translation"},
{"arg":"scaleZ","optional":true,"default":"1","description":"z-axis scale factor"},
{"arg":"scaleM","optional":true,"default":"1","description":"m-axis scale factor"}
],
"examples": [
{ "expression":"geom_to_wkt(affine_transform(geom_from_wkt('LINESTRING(1 1, 2 2)'), 2, 2, 0, 1, 1))", "returns":"'LineString (3 3, 4 4)'"},
{ "expression":"geom_to_wkt(affine_transform(geom_from_wkt('POLYGON((0 0, 0 3, 2 2, 0 0))'), 0, 0, -90, 1, 2))", "returns":"'Polygon ((0 0, 6 0, 4 -2, 0 0))'"},
{ "expression":"geom_to_wkt(affine_transform(geom_from_wkt('POINT(3 1)'), 0, 0, 0, 1, 1, 5, 0))", "returns":"'PointZ (3 1 5)'"}
]
}
51 changes: 51 additions & 0 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -4006,6 +4006,46 @@ static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContex
return QVariant::fromValue( fGeom );
}

static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
if ( fGeom.isNull() )
{
return QVariant();
}

const double deltaX = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
const double deltaY = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );

const double rotationZ = QgsExpressionUtils::getDoubleValue( values.at( 3 ), parent );

const double scaleX = QgsExpressionUtils::getDoubleValue( values.at( 4 ), parent );
const double scaleY = QgsExpressionUtils::getDoubleValue( values.at( 5 ), parent );

const double deltaZ = QgsExpressionUtils::getDoubleValue( values.at( 6 ), parent );
const double deltaM = QgsExpressionUtils::getDoubleValue( values.at( 7 ), parent );
const double scaleZ = QgsExpressionUtils::getDoubleValue( values.at( 8 ), parent );
const double scaleM = QgsExpressionUtils::getDoubleValue( values.at( 9 ), parent );

if ( deltaZ != 0.0 && !fGeom.constGet()->is3D() )
{
fGeom.get()->addZValue( 0 );
}
if ( deltaM != 0.0 && !fGeom.constGet()->isMeasure() )
{
fGeom.get()->addMValue( 0 );
}

QTransform transform;
transform.translate( deltaX, deltaY );
transform.rotate( rotationZ );
transform.scale( scaleX, scaleY );
fGeom.transform( transform, deltaZ, scaleZ, deltaM, scaleM );

return QVariant::fromValue( fGeom );
}


static QVariant fcnCentroid( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
Expand Down Expand Up @@ -6972,6 +7012,17 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
fcnRotate, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "deltaX" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "deltaY" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "rotationZ" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "scaleX" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "scaleY" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "deltaZ" ), true, 0 )
<< QgsExpressionFunction::Parameter( QStringLiteral( "deltaM" ), true, 0 )
<< QgsExpressionFunction::Parameter( QStringLiteral( "scaleZ" ), true, 1 )
<< QgsExpressionFunction::Parameter( QStringLiteral( "scaleM" ), true, 1 ),
fcnAffineTransform, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "buffer" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "distance" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "segments" ), true, 8 ),
Expand Down
7 changes: 7 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -1305,6 +1305,13 @@ class TestQgsExpression: public QObject
QTest::newRow( "rotate line fixed multi point" ) << "geom_to_wkt(rotate(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),90, geom_from_wkt('MULTIPOINT((-5 -3))')))" << false << QVariant( "LineString (-2 -8, -2 -18, 8 -18)" );
QTest::newRow( "rotate line fixed multi point multiple" ) << "geom_to_wkt(rotate(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),90, geom_from_wkt('MULTIPOINT(-5 -3,1 2)')))" << true << QVariant();
QTest::newRow( "rotate polygon centroid" ) << "geom_to_wkt(rotate(geom_from_wkt('Polygon((0 0, 10 0, 10 10, 0 0))'),-90))" << false << QVariant( "Polygon ((10 0, 10 10, 0 10, 10 0))" );
QTest::newRow( "affine_transform not geom" ) << "affine_transform('g', 0, 0, 0, 0, 0, 0)" << true << QVariant();
QTest::newRow( "affine_transform null" ) << "affine_transform(NULL, 0, 0, 0, 0, 0, 0)" << false << QVariant();
QTest::newRow( "affine_transform point XYZM" ) << "geom_to_wkt(affine_transform(geom_from_wkt('POINT(2 2 2 2)'), 2, 2, 180, 0, 1, 1, 1, 2, 2))" << false << QVariant( "PointZM (2 0 5 5)" );
QTest::newRow( "affine_transform point with negative scale" ) << "geom_to_wkt(affine_transform(geom_from_wkt('POINT(1 1)'), 0, 0, 90, -2, -2))" << false << QVariant( "Point (2 -2)" );
QTest::newRow( "affine_transform line XY" ) << "geom_to_wkt(affine_transform(geom_from_wkt('LINESTRING(1 0, 2 0)'), 0, 0, 90, 2, 1))" << false << QVariant( "LineString (0 2, 0 4)" );
QTest::newRow( "affine_transform polygon XYZ" ) << "geom_to_wkt(affine_transform(geom_from_wkt('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))'), 0, 0, -90, 0.5, 0.5))" << false << QVariant( "Polygon ((0 0, 0.5 0, 0.5 -0.5, 0 -0.5, 0 0))" );
QTest::newRow( "affine_transform point XY with translation on ZM" ) << "geom_to_wkt(affine_transform(geom_from_wkt('POINT(1 1)'), 0, 0, 0, 1, 1, 3, 4))" << false << QVariant( "PointZM (1 1 3 4)" );
QTest::newRow( "is_multipart true" ) << "is_multipart(geom_from_wkt('MULTIPOINT ((0 0),(1 1),(2 2))'))" << false << QVariant( true );
QTest::newRow( "is_multipart false" ) << "is_multipart(geom_from_wkt('POINT (0 0)'))" << false << QVariant( false );
QTest::newRow( "is_multipart false empty geometry" ) << "is_multipart(geom_from_wkt('POINT EMPTY'))" << false << QVariant( false );
Expand Down

0 comments on commit 9e38e43

Please sign in to comment.