Skip to content

Commit

Permalink
[feature] Add expression for square by diagonal and rectangle from 3 …
Browse files Browse the repository at this point in the history
…points

With the new class QgsQuadrilateral, we can add expressions to create a square by a diagonal and rectangles by 3 points
  • Loading branch information
lbartoletti authored and nyalldawson committed Jan 16, 2019
1 parent 200d4f6 commit aaede28
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 2 deletions.
17 changes: 17 additions & 0 deletions resources/function_help/json/make_rectangle_3points
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "make_rectangle_3points",
"type": "function",
"description": "Creates a rectangle from 3 points.",
"variableLenArguments": true,
"arguments": [
{"arg":"point1", "description": "First point."},
{"arg":"point2", "description": "Second point."},
{"arg":"point3", "description": "Third point."},
{"arg":"option", "optional": true, "default": "0", "description":
"An optional argument to construct the rectangle. By default this value is 0. Value can be 0 (distance) or 1 (projected). Option distance: Second distance is equal to the distance between 2nd and 3rd point. Option projected: Second distance is equal to the distance of the perpendicular projection of the 3rd point on the segment or its extension."}
],
"examples": [
{ "expression":"geom_to_wkt(make_rectangle(make_point(0, 0), make_point(0,5), make_point(5, 5), 0)))", "returns":"'Polygon ((0 0, 0 5, 5 5, 5 0, 0 0))'"},
{ "expression":"geom_to_wkt(make_rectangle(make_point(0, 0), make_point(0,5), make_point(5, 3), 1)))", "returns":"'Polygon ((0 0, 0 5, 5 5, 5 0, 0 0))'"}
]
}
14 changes: 14 additions & 0 deletions resources/function_help/json/make_square
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "make_square",
"type": "function",
"description": "Creates a square from a diagonal.",
"variableLenArguments": true,
"arguments": [
{"arg":"point1", "description": "First point of the regular polygon"},
{"arg":"point2", "description": "Second point"}
],
"examples": [
{ "expression":"geom_to_wkt(make_square( make_point(0,0), make_point(5,5)))", "returns":"'Polygon ((0 0, -0 5, 5 5, 5 0, 0 0))'"},
{ "expression":"geom_to_wkt(make_square( make_point(5,0), make_point(5,5)))", "returns":"'Polygon ((5 0, 2.5 2.5, 5 5, 7.5 2.5, 5 0))'"}
]
}
68 changes: 66 additions & 2 deletions src/core/expression/qgsexpressionfunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "qgstriangle.h"
#include "qgscurve.h"
#include "qgsregularpolygon.h"
#include "qgsquadrilateral.h"
#include "qgsmultipolygon.h"
#include "qgsogcutils.h"
#include "qgsdistancearea.h"
Expand Down Expand Up @@ -2487,6 +2488,60 @@ static QVariant fcnMakeRegularPolygon( const QVariantList &values, const QgsExpr

}

static QVariant fcnMakeSquare( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
if ( pt1.isNull() )
return QVariant();
if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
return QVariant();

QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
if ( pt2.isNull() )
return QVariant();
if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
return QVariant();

const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
QgsQuadrilateral square = QgsQuadrilateral::squareFromDiagonal( *point1, *point2 );

return QVariant::fromValue( QgsGeometry( square.toPolygon() ) );
}

static QVariant fcnMakeRectangleFrom3Points( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
{
QgsGeometry pt1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
if ( pt1.isNull() )
return QVariant();
if ( pt1.type() != QgsWkbTypes::PointGeometry || pt1.isMultipart() )
return QVariant();

QgsGeometry pt2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
if ( pt2.isNull() )
return QVariant();
if ( pt2.type() != QgsWkbTypes::PointGeometry || pt2.isMultipart() )
return QVariant();

QgsGeometry pt3 = QgsExpressionUtils::getGeometry( values.at( 2 ), parent );
if ( pt3.isNull() )
return QVariant();
if ( pt3.type() != QgsWkbTypes::PointGeometry || pt3.isMultipart() )
return QVariant();

QgsQuadrilateral::ConstructionOption option = static_cast< QgsQuadrilateral::ConstructionOption >( QgsExpressionUtils::getIntValue( values.at( 3 ), parent ) );
if ( ( option < QgsQuadrilateral::Distance ) || ( option > QgsQuadrilateral::Projected ) )
{
parent->setEvalErrorString( QObject::tr( "Option can be 0 (distance) or 1 (projected)" ) );
return QVariant();
}
const QgsPoint *point1 = qgsgeometry_cast< const QgsPoint *>( pt1.constGet() );
const QgsPoint *point2 = qgsgeometry_cast< const QgsPoint *>( pt2.constGet() );
const QgsPoint *point3 = qgsgeometry_cast< const QgsPoint *>( pt3.constGet() );
QgsQuadrilateral rect = QgsQuadrilateral::rectangleFrom3Points( *point1, *point2, *point3, option );
return QVariant::fromValue( QgsGeometry( rect.toPolygon() ) );
}

static QVariant pointAt( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) // helper function
{
FEAT_FROM_CONTEXT( context, f );
Expand Down Expand Up @@ -4888,8 +4943,17 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
<< QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "number_sides" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "circle" ), true, 0 ),
fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) );

fcnMakeRegularPolygon, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "make_square" ), QgsExpressionFunction::ParameterList()
<< QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) ),
fcnMakeSquare, QStringLiteral( "GeometryGroup" ) )
<< new QgsStaticExpressionFunction( QStringLiteral( "make_rectangle_3points" ), QgsExpressionFunction::ParameterList()
<< QgsExpressionFunction::Parameter( QStringLiteral( "point1" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "point2" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "point3" ) )
<< QgsExpressionFunction::Parameter( QStringLiteral( "option" ), true, 0 ),
fcnMakeRectangleFrom3Points, QStringLiteral( "GeometryGroup" ) );
QgsStaticExpressionFunction *xAtFunc = new QgsStaticExpressionFunction( QStringLiteral( "$x_at" ), 1, fcnXat, QStringLiteral( "GeometryGroup" ), QString(), true, QSet<QString>(), false, QStringList() << QStringLiteral( "xat" ) << QStringLiteral( "x_at" ) );
xAtFunc->setIsStatic( false );
sFunctions << xAtFunc;
Expand Down
15 changes: 15 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,21 @@ class TestQgsExpression: public QObject
QTest::newRow( "make_regular_polygon bad (numEdges < 3)" ) << "make_regular_polygon(make_point(0,0), make_point(0,5), 2)" << true << QVariant();
QTest::newRow( "make_regular_polygon" ) << "geom_to_wkt(make_regular_polygon(make_point(0,0), make_point(0,5), 5), 2)" << false << QVariant( "Polygon ((0 5, 4.76 1.55, 2.94 -4.05, -2.94 -4.05, -4.76 1.55, 0 5))" );
QTest::newRow( "make_regular_polygon" ) << "geom_to_wkt(make_regular_polygon(make_point(0,0), project(make_point(0,0), 4.0451, radians(36)), 5, 1), 2)" << false << QVariant( "Polygon ((0 5, 4.76 1.55, 2.94 -4.05, -2.94 -4.05, -4.76 1.55, 0 5))" );
QTest::newRow( "make_square not geom (point 1)" ) << "make_square(make_line(make_point(1,2), make_point(3,4)), make_point(5,5))" << false << QVariant();
QTest::newRow( "make_square not geom (point 2)" ) << "make_square(make_point(0,0), make_line(make_point(1,2), make_point(3,4)))" << false << QVariant();
QTest::newRow( "make_square bad (point 1)" ) << "make_square('a', make_point(5,5))" << true << QVariant();
QTest::newRow( "make_square bad (point 2)" ) << "make_square(make_point(0,0), 'a')" << true << QVariant();
QTest::newRow( "make_square" ) << "geom_to_wkt(make_square(make_point(5, 5), make_point(1, 1)))" << false << QVariant( "Polygon ((5 5, 5 1, 1 1, 1 5, 5 5))" );
QTest::newRow( "make_rectangle_3points not geom (point 1)" ) << "make_rectangle_3points( make_line(make_point(1,2), make_point(3,4)), make_point(0,5), make_point(5,5))" << false << QVariant();
QTest::newRow( "make_rectangle_3points not geom (point 2)" ) << "make_rectangle_3points(make_point(0,0), make_line(make_point(1,2), make_point(3,4)), make_point(5,5))" << false << QVariant();
QTest::newRow( "make_rectangle_3points not geom (point 3)" ) << "make_rectangle_3points(make_point(0,0), make_point(0,5), make_line(make_point(1,2), make_point(3,4)))" << false << QVariant();
QTest::newRow( "make_rectangle_3points bad (point 1)" ) << "make_rectangle_3points('a', make_point(0,5), make_point(5,5))" << true << QVariant();
QTest::newRow( "make_rectangle_3points bad (point 2)" ) << "make_rectangle_3points(make_point(0,0), 'a', make_point(5,5))" << true << QVariant();
QTest::newRow( "make_rectangle_3points bad (point 3)" ) << "make_rectangle_3points(make_point(0,0), make_point(0,5), 'a')" << true << QVariant();
QTest::newRow( "make_rectangle_3points bad (invalid option)" ) << "make_rectangle_3points(make_point(0,0), make_point(0,5), make_point(5,5), 2)" << true << QVariant();
QTest::newRow( "make_rectangle_3points (distance default)" ) << "geom_to_wkt(make_rectangle_3points(make_point(0, 0), make_point(0,5), make_point(5, 5)))" << false << QVariant( "Polygon ((0 0, 0 5, 5 5, 5 0, 0 0))" );
QTest::newRow( "make_rectangle_3points (distance)" ) << "geom_to_wkt(make_rectangle_3points(make_point(0, 0), make_point(0,5), make_point(5, 5), 0))" << false << QVariant( "Polygon ((0 0, 0 5, 5 5, 5 0, 0 0))" );
QTest::newRow( "make_rectangle_3points (projected)" ) << "geom_to_wkt(make_rectangle_3points(make_point(0, 0), make_point(0,5), make_point(5, 3), 1))" << false << QVariant( "Polygon ((0 0, 0 5, 5 5, 5 0, 0 0))" );
QTest::newRow( "x point" ) << "x(make_point(2.2,4.4))" << false << QVariant( 2.2 );
QTest::newRow( "y point" ) << "y(make_point(2.2,4.4))" << false << QVariant( 4.4 );
QTest::newRow( "z point" ) << "z(make_point(2.2,4.4,6.6))" << false << QVariant( 6.6 );
Expand Down

0 comments on commit aaede28

Please sign in to comment.