Skip to content

Commit

Permalink
Merge pull request #3451 from nyalldawson/smoooooth
Browse files Browse the repository at this point in the history
[FEATURE] New simplify + smoothing expression functions
  • Loading branch information
nyalldawson committed Sep 4, 2016
2 parents 0036274 + 679797e commit 95c4fa1
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 0 deletions.
9 changes: 9 additions & 0 deletions resources/function_help/json/simplify
@@ -0,0 +1,9 @@
{
"name": "simplify",
"type": "function",
"description":"Simplifies a geometry by removing nodes using a distance based threshold (ie, the Douglas Peucker algorithm). The algorithm preserves large deviations in geometries and reduces the number of vertices in nearly straight segments.",
"arguments": [ {"arg":"geometry","description":"a geometry"},
{"arg":"tolerance","description":"maximum deviation from straight segments for points to be removed"} ],
"examples": [ { "expression":"geom_to_wkt(simplify(geometry:=geom_from_wkt('LineString(0 0, 5 0.1, 10 0)'),tolerance:=5))", "returns":"'LineString(0 0, 10 0)'"}]
}

9 changes: 9 additions & 0 deletions resources/function_help/json/simplify_vw
@@ -0,0 +1,9 @@
{
"name": "simplify_vw",
"type": "function",
"description":"Simplifies a geometry by removing nodes using an area based threshold (ie, the Visvalingam-Whyatt algorithm). The algorithm removes vertices which create small areas in geometries, eg narrow spikes or nearly straight segments.",
"arguments": [ {"arg":"geometry","description":"a geometry"},
{"arg":"tolerance","description":"a measure of the maximum area created by a node for the node to be removed"} ],
"examples": [ { "expression":"geom_to_wkt(simplify_vw(geometry:=geom_from_wkt('LineString(0 0, 5 0, 5.01 10, 5.02 0, 10 0)'),tolerance:=5))", "returns":"'LineString(0 0, 10 0)'"}]
}

12 changes: 12 additions & 0 deletions resources/function_help/json/smooth
@@ -0,0 +1,12 @@
{
"name": "smooth",
"type": "function",
"description":"Smooths a geometry by adding extra nodes which round off corners in the geometry.",
"arguments": [ {"arg":"geometry","description":"a geometry"},
{"arg":"iterations", "optional":true,"description":"number of smoothing iterations to apply. Larger numbers result in smoother but more complex geometries."},
{"arg":"offset", "optional":true,"description":"value between 0 and 0.5 which controls how tightly the smoothed geometry follow the original geometry. Smaller values result in a tighter smoothing, larger values result in looser smoothing."},
{"arg":"min_length", "optional":true,"description":"minimum length of segments to apply smoothing to. This parameter can be used to avoid placing excessive additional nodes in shorter segments of the geometry."},
{"arg":"max_angle", "optional":true,"description":"maximum angle at node for smoothing to be applied (0-180). By lowering the maximum angle intentionally sharp corners in the geometry can be preserved. For instance, a value of 80 degrees will retain right angles in the geometry."} ],
"examples": [ { "expression":"geom_to_wkt(smooth(geometry:=geom_from_wkt('LineString(0 0, 5 0, 5 5)'),iterations:=1,offset:=0.2,min_length:=-1,max_angle:=180))", "returns":"'LineString (0 0, 4 0, 5 1, 5 5)'"}]
}

61 changes: 61 additions & 0 deletions src/core/qgsexpression.cpp
Expand Up @@ -51,6 +51,7 @@
#include "qgscurvepolygon.h"
#include "qgsexpressionprivate.h"
#include "qgsexpressionsorter.h"
#include "qgsmaptopixelgeometrysimplifier.h"

#if QT_VERSION < 0x050000
#include <qtextdocument.h>
Expand Down Expand Up @@ -1767,6 +1768,59 @@ static QVariant fcnLineMerge( const QVariantList& values, const QgsExpressionCon
return QVariant::fromValue( merged );
}

static QVariant fcnSimplify( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );

if ( geom.isEmpty() )
return QVariant();

double tolerance = getDoubleValue( values.at( 1 ), parent );

QgsGeometry simplified = geom.simplify( tolerance );
if ( simplified.isEmpty() )
return QVariant();

return simplified;
}

static QVariant fcnSimplifyVW( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );

if ( geom.isEmpty() )
return QVariant();

double tolerance = getDoubleValue( values.at( 1 ), parent );

QgsMapToPixelSimplifier simplifier( QgsMapToPixelSimplifier::SimplifyGeometry, tolerance, QgsMapToPixelSimplifier::Visvalingam );

QgsGeometry simplified = simplifier.simplify( geom );
if ( simplified.isEmpty() )
return QVariant();

return simplified;
}

static QVariant fcnSmooth( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );

if ( geom.isEmpty() )
return QVariant();

int iterations = qMin( getIntValue( values.at( 1 ), parent ), 10 );
double offset = qBound( 0.0, getDoubleValue( values.at( 2 ), parent ), 0.5 );
double minLength = getDoubleValue( values.at( 3 ), parent );
double maxAngle = qBound( 0.0, getDoubleValue( values.at( 4 ), parent ), 180.0 );

QgsGeometry smoothed = geom.smooth( iterations, offset, minLength, maxAngle );
if ( smoothed.isEmpty() )
return QVariant();

return smoothed;
}

static QVariant fcnMakePoint( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
if ( values.count() < 2 || values.count() > 4 )
Expand Down Expand Up @@ -3166,6 +3220,7 @@ const QStringList& QgsExpression::BuiltinFunctions()
<< "disjoint" << "intersects" << "touches" << "crosses" << "contains"
<< "relate"
<< "overlaps" << "within" << "buffer" << "offset_curve" << "single_sided_buffer"
<< "simplify" << "simplify_vw" << "smooth"
<< "centroid" << "bounds" << "reverse" << "exterior_ring"
<< "boundary" << "line_merge"
<< "bounds_width" << "bounds_height" << "is_closed" << "convex_hull" << "difference"
Expand Down Expand Up @@ -3377,6 +3432,12 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "boundary", ParameterList() << Parameter( "geometry" ), fcnBoundary, "GeometryGroup" )
<< new StaticFunction( "line_merge", ParameterList() << Parameter( "geometry" ), fcnLineMerge, "GeometryGroup" )
<< new StaticFunction( "bounds", 1, fcnBounds, "GeometryGroup" )
<< new StaticFunction( "simplify", ParameterList() << Parameter( "geometry" ) << Parameter( "tolerance" ), fcnSimplify, "GeometryGroup" )
<< new StaticFunction( "simplify_vw", ParameterList() << Parameter( "geometry" ) << Parameter( "tolerance" ), fcnSimplifyVW, "GeometryGroup" )
<< new StaticFunction( "smooth", ParameterList() << Parameter( "geometry" ) << Parameter( "iterations", true, 1 )
<< Parameter( "offset", true, 0.25 )
<< Parameter( "min_length", true, -1 )
<< Parameter( "max_angle", true, 180 ), fcnSmooth, "GeometryGroup" )
<< new StaticFunction( "num_points", 1, fcnGeomNumPoints, "GeometryGroup" )
<< new StaticFunction( "num_interior_rings", 1, fcnGeomNumInteriorRings, "GeometryGroup" )
<< new StaticFunction( "num_rings", 1, fcnGeomNumRings, "GeometryGroup" )
Expand Down
12 changes: 12 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -798,6 +798,18 @@ class TestQgsExpression: public QObject
QTest::newRow( "distance_to_vertex null" ) << "distance_to_vertex(NULL, 0)" << false << QVariant();
QTest::newRow( "distance_to_vertex point" ) << "distance_to_vertex(geom_from_wkt('POINT(1 2)'),0)" << false << QVariant( 0.0 );
QTest::newRow( "distance_to_vertex line" ) << "distance_to_vertex(geometry:=geom_from_wkt('LineString(0 0, 10 0, 10 10)'),vertex:=1)" << false << QVariant( 10.0 );
QTest::newRow( "simplify not geom" ) << "simplify('g',5)" << true << QVariant();
QTest::newRow( "simplify null" ) << "simplify(NULL,5)" << false << QVariant();
QTest::newRow( "simplify point" ) << "geom_to_wkt(simplify(geom_from_wkt('POINT(1 2)'),10))" << false << QVariant( "Point (1 2)" );
QTest::newRow( "simplify line" ) << "geom_to_wkt(simplify(geometry:=geom_from_wkt('LineString(0 0, 5 0, 10 0)'),tolerance:=5))" << false << QVariant( "LineString (0 0, 10 0)" );
QTest::newRow( "simplify_vw not geom" ) << "simplify_vw('g',5)" << true << QVariant();
QTest::newRow( "simplify_vw null" ) << "simplify_vw(NULL,5)" << false << QVariant();
QTest::newRow( "simplify_vw point" ) << "geom_to_wkt(simplify_vw(geom_from_wkt('POINT(1 2)'),10))" << false << QVariant( "Point (1 2)" );
QTest::newRow( "simplify_vw line" ) << "geom_to_wkt(simplify_vw(geometry:=geom_from_wkt('LineString(0 0, 5 0, 5.01 10, 5.02 0, 10 0)'),tolerance:=5))" << false << QVariant( "LineString (0 0, 10 0)" );
QTest::newRow( "smooth not geom" ) << "smooth('g',5)" << true << QVariant();
QTest::newRow( "smooth null" ) << "smooth(NULL,5)" << false << QVariant();
QTest::newRow( "smooth point" ) << "geom_to_wkt(smooth(geom_from_wkt('POINT(1 2)'),10))" << false << QVariant( "Point (1 2)" );
QTest::newRow( "smooth line" ) << "geom_to_wkt(smooth(geometry:=geom_from_wkt('LineString(0 0, 5 0, 5 5)'),iterations:=1,offset:=0.2,min_length:=-1,max_angle:=180))" << false << QVariant( "LineString (0 0, 4 0, 5 1, 5 5)" );

// string functions
QTest::newRow( "lower" ) << "lower('HeLLo')" << false << QVariant( "hello" );
Expand Down

0 comments on commit 95c4fa1

Please sign in to comment.