Skip to content

Commit

Permalink
[feature][expressions] Add new "scale" function for scaling a geometry
Browse files Browse the repository at this point in the history
Just like the 'rotate' function, 'scale' accepts an optional point
to apply the scaling from. If not specified, scaling is done
from the centre of the geometry's bounding box.
  • Loading branch information
nyalldawson committed Oct 22, 2021
1 parent 7c03c9e commit deba02b
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 0 deletions.
13 changes: 13 additions & 0 deletions resources/function_help/json/scale
@@ -0,0 +1,13 @@
{
"name": "scale",
"type": "function",
"groups": ["GeometryGroup"],
"description": "Returns a scaled version of a geometry. Calculations are in the Spatial Reference System of this geometry.",
"arguments": [ {"arg":"geometry","description":"a geometry"},
{"arg":"x_scale","description":"x-axis scaling factor"},
{"arg":"y_scale","description":"y-axis scaling factor"},
{"arg":"center", "optional":true,"description":"scaling center point. If not specified, the center of the geometry's bounding box is used."}
],
"examples": [ { "expression":"scale($geometry, 2, 0.5, make_point(4, 5))", "returns":"geometry scaled twice horizontally and halved vertically, around the (4, 5) point"},
{ "expression":"scale($geometry, 2, 0.5)", "returns":"geometry twice horizontally and halved vertically, around the center of its bounding box"}]
}
36 changes: 36 additions & 0 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -4134,6 +4134,37 @@ static QVariant fcnRotate( const QVariantList &values, const QgsExpressionContex
return QVariant::fromValue( fGeom );
}

static QVariant fcnScale( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
const double xScale = QgsExpressionUtils::getDoubleValue( values.at( 1 ), parent );
const double yScale = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
const QgsGeometry center = values.at( 3 ).isValid() ? QgsExpressionUtils::getGeometry( values.at( 3 ), parent )
: QgsGeometry();

QgsPointXY pt;
if ( center.isNull() )
{
// if center wasn't specified, use bounding box centroid
pt = fGeom.boundingBox().center();
}
else if ( QgsWkbTypes::flatType( center.constGet()->simplifiedTypeRef()->wkbType() ) != QgsWkbTypes::Point )
{
parent->setEvalErrorString( QObject::tr( "Function 'scale' requires a point value for the center" ) );
return QVariant();
}
else
{
pt = center.asPoint();
}

QTransform t = QTransform::fromTranslate( pt.x(), pt.y() );
t.scale( xScale, yScale );
t.translate( -pt.x(), -pt.y() );
fGeom.transform( t );
return QVariant::fromValue( fGeom );
}

static QVariant fcnAffineTransform( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
Expand Down Expand Up @@ -7140,6 +7171,11 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "rotation" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
fcnRotate, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "scale" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "x_scale" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "y_scale" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "center" ), true ),
fcnScale, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "affine_transform" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "delta_x" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "delta_y" ) )
Expand Down
10 changes: 10 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -1322,6 +1322,16 @@ 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( "scale not geom" ) << "scale('g', 1.2, 0.8)" << true << QVariant();
QTest::newRow( "scale null" ) << "scale(NULL, 1.2, 0.8)" << false << QVariant();
QTest::newRow( "scale point" ) << "geom_to_wkt(scale(geom_from_wkt('POINT( 20 10)'), 1.2, 0.8, geom_from_wkt('POINT( 30 15)')))" << false << QVariant( "Point (18 11)" );
QTest::newRow( "scale line centroid" ) << "geom_to_wkt(scale(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),1.2, 0.8))" << false << QVariant( "LineString (-1 1, 11 1, 11 9)" );
QTest::newRow( "scale line fixed point" ) << "geom_to_wkt(scale(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),1.2, 0.8, make_point(5, 2)))" << false << QVariant( "LineString (-1 0.4, 11 0.4, 11 8.4)" );
QTest::newRow( "scale line fixed point not geom" ) << "geom_to_wkt(scale(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),1.2, 0.8, 'a'))" << true << QVariant();
QTest::newRow( "scale line fixed point not point" ) << "geom_to_wkt(scale(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),1.2, 0.8, geom_from_wkt('LineString(0 0, 10 0, 10 10)')))" << true << QVariant();
QTest::newRow( "scale line fixed multi point" ) << "geom_to_wkt(scale(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),1.2, 0.8, geom_from_wkt('MULTIPOINT((-5 -3))')))" << false << QVariant( "LineString (1 -0.6, 13 -0.6, 13 7.4)" );
QTest::newRow( "scale line fixed multi point multiple" ) << "geom_to_wkt(scale(geom_from_wkt('LineString(0 0, 10 0, 10 10)'),1.2, 0.8, geom_from_wkt('MULTIPOINT(-5 -3,1 2)')))" << true << QVariant();
QTest::newRow( "scale polygon centroid" ) << "geom_to_wkt(scale(geom_from_wkt('Polygon((0 0, 10 0, 10 10, 0 0))'), 1.2, 0.8))" << false << QVariant( "Polygon ((-1 1, 11 1, 11 9, -1 1))" );
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)" );
Expand Down

0 comments on commit deba02b

Please sign in to comment.