Skip to content
Permalink
Browse files
[feature][expressions] Add new "scale" function for scaling a geometry
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 deba02b16236a276c3323d3d316dfcf6710a868c
Showing with 59 additions and 0 deletions.
  1. +13 −0 resources/function_help/json/scale
  2. +36 −0 src/core/expression/qgsexpressionfunction.cpp
  3. +10 −0 tests/src/core/testqgsexpression.cpp
@@ -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"}]
}
@@ -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 );
@@ -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" ) )
@@ -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)" );

0 comments on commit deba02b

Please sign in to comment.