Skip to content

Commit

Permalink
Add extrude function to QgsGeometry
Browse files Browse the repository at this point in the history
  • Loading branch information
m-kuhn committed Jan 14, 2016
1 parent e290c98 commit 5425410
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 7 deletions.
5 changes: 4 additions & 1 deletion python/core/geometry/qgsgeometry.sip
Expand Up @@ -433,9 +433,12 @@ class QgsGeometry
/** Returns a geometry representing the points making up this geometry that do not make up other. */
QgsGeometry* difference( const QgsGeometry* geometry ) const /Factory/;

/** Returns a Geometry representing the points making up this Geometry that do not make up other. */
/** Returns a Geometry representing the points making up this geometry that do not make up other. */
QgsGeometry* symDifference( const QgsGeometry* geometry ) const /Factory/;

/** Returns an extruded version of this geometry. */
QgsGeometry extrude( double x, double y );

/** Exports the geometry to WKT
* @note precision parameter added in 2.4
* @return true in case of success and false else
Expand Down
20 changes: 20 additions & 0 deletions resources/function_help/json/extrude
@@ -0,0 +1,20 @@
{
"name": "extrude",
"type": "function",
"description": "Returns an extruded version of the input (Multi-)Curve or (Multi-)Linestring geometry with an extension specified by x and y.",
"arguments": [
{"arg":"geom","description":"a polygon geometry"},
{"arg":"x","description":"x extension, numeric value"},
{"arg":"y","description":"y extension, numeric value"}
],
"examples": [
{
"expression":"extrude(geom_from_wkt('LineString(1 2, 3 2, 4 3)'), 1, 2)",
"returns":"Polygon ((1 2, 3 2, 4 3, 5 5, 4 4, 2 4, 1 2))"
},
{
"expression":"extrude(geom_from_wkt('MultiLineString((1 2, 3 2), (4 3, 8 3)'), 1, 2)",
"returns":"MultiPolygon (((1 2, 3 2, 4 4, 2 4, 1 2)),((4 3, 8 3, 9 5, 5 5, 4 3)))"
}
]
}
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -320,6 +320,7 @@ SET(QGIS_CORE_SRCS
geometry/qgsgeometry.cpp
geometry/qgsgeometrycollectionv2.cpp
geometry/qgsgeometryeditutils.cpp
geometry/qgsinternalgeometryengine.cpp
geometry/qgsgeometryfactory.cpp
geometry/qgsgeometryutils.cpp
geometry/qgsgeos.cpp
Expand Down Expand Up @@ -788,6 +789,7 @@ SET(QGIS_CORE_HDRS
layertree/qgslayertreeutils.h

geometry/qgsgeometry.h
geometry/qgsinternalgeometryengine.h
geometry/qgsabstractgeometryv2.h
geometry/qgswkbtypes.h
geometry/qgspointv2.h
Expand Down
8 changes: 8 additions & 0 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -23,6 +23,7 @@ email : morb at ozemail dot com dot au
#include "qgsgeometryeditutils.h"
#include "qgsgeometryfactory.h"
#include "qgsgeometryutils.h"
#include "qgsinternalgeometryengine.h"
#include "qgsgeos.h"
#include "qgsapplication.h"
#include "qgslogger.h"
Expand Down Expand Up @@ -1404,6 +1405,13 @@ QgsGeometry* QgsGeometry::symDifference( const QgsGeometry* geometry ) const
return new QgsGeometry( resultGeom );
}

QgsGeometry QgsGeometry::extrude( double x, double y )
{
QgsInternalGeometryEngine engine( *this );

return engine.extrude( x, y );
}

QList<QgsGeometry*> QgsGeometry::asGeometryCollection() const
{
QList<QgsGeometry*> geometryList;
Expand Down
5 changes: 4 additions & 1 deletion src/core/geometry/qgsgeometry.h
Expand Up @@ -480,9 +480,12 @@ class CORE_EXPORT QgsGeometry
/** Returns a geometry representing the points making up this geometry that do not make up other. */
QgsGeometry* difference( const QgsGeometry* geometry ) const;

/** Returns a Geometry representing the points making up this Geometry that do not make up other. */
/** Returns a geometry representing the points making up this geometry that do not make up other. */
QgsGeometry* symDifference( const QgsGeometry* geometry ) const;

/** Returns an extruded version of this geometry. */
QgsGeometry extrude( double x, double y );

/** Exports the geometry to WKT
* @note precision parameter added in 2.4
* @return true in case of success and false else
Expand Down
6 changes: 3 additions & 3 deletions src/core/geometry/qgsgeometryengine.h
Expand Up @@ -13,8 +13,8 @@ email : marco.hugentobler at sourcepole dot com
* *
***************************************************************************/

#ifndef QGSVECTORTOPOLOGY_H
#define QGSVECTORTOPOLOGY_H
#ifndef QGSGEOMETRYENGINE_H
#define QGSGEOMETRYENGINE_H

#include "qgspointv2.h"
#include "qgslinestringv2.h"
Expand Down Expand Up @@ -106,4 +106,4 @@ class CORE_EXPORT QgsGeometryEngine
QgsGeometryEngine();
};

#endif // QGSVECTORTOPOLOGY_H
#endif // QGSGEOMETRYENGINE_H
88 changes: 88 additions & 0 deletions src/core/geometry/qgsinternalgeometryengine.cpp
@@ -0,0 +1,88 @@
/***************************************************************************
qgsinternalgeometryengine.cpp - QgsInternalGeometryEngine
---------------------
begin : 13.1.2016
copyright : (C) 2016 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsinternalgeometryengine.h"

#include "qgslinestringv2.h"
#include "qgsmultipolygonv2.h"
#include "qgspolygonv2.h"
#include "qgsmulticurvev2.h"

#include <QTransform>

QgsInternalGeometryEngine::QgsInternalGeometryEngine( const QgsGeometry& geometry )
: mGeometry( geometry.geometry() )
{

}

/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
* full unit tests.
* See details in QEP #17
****************************************************************************/

QgsGeometry QgsInternalGeometryEngine::extrude( double x, double y )
{
QList<QgsLineStringV2*> linesToProcess;

const QgsMultiCurveV2* multiCurve = dynamic_cast< const QgsMultiCurveV2* >( mGeometry );
if ( multiCurve )
{
for ( int i = 0; i < multiCurve->partCount(); ++i )
{
linesToProcess << static_cast<QgsLineStringV2*>( multiCurve->geometryN( i )->clone() );
}
}

const QgsCurveV2* curve = dynamic_cast< const QgsCurveV2* >( mGeometry );
if ( curve )
{
linesToProcess << static_cast<QgsLineStringV2*>( curve->segmentize() );
}

QgsMultiPolygonV2* multipolygon = linesToProcess.size() > 1 ? new QgsMultiPolygonV2() : nullptr;
QgsPolygonV2* polygon;

if ( !linesToProcess.empty() )
{
Q_FOREACH ( QgsLineStringV2* line, linesToProcess )
{
QTransform transform = QTransform::fromTranslate( x, y );

QgsLineStringV2* secondline = line->reversed();
secondline->transform( transform );

line->append( secondline );
line->addVertex( line->pointN( 0 ) );

polygon = new QgsPolygonV2();
polygon->setExteriorRing( line );

if ( multipolygon )
multipolygon->addGeometry( polygon );

delete secondline;
}

if ( multipolygon )
return QgsGeometry( multipolygon );
else
return QgsGeometry( polygon );
}

return QgsGeometry();
}
54 changes: 54 additions & 0 deletions src/core/geometry/qgsinternalgeometryengine.h
@@ -0,0 +1,54 @@
/***************************************************************************
qgsinternalgeometryengine.h - QgsInternalGeometryEngine
---------------------
begin : 13.1.2016
copyright : (C) 2016 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSINTERNALGEOMETRYENGINE_H
#define QGSINTERNALGEOMETRYENGINE_H

#include "qgsgeometry.h"

/**
* This class offers geometry processing methods.
*
* The methods are available via QgsGeometry::[geometryfunction]
* and therefore this does not need to be accessed directly.
*
* @note not available in Python bindings
*/

class QgsInternalGeometryEngine
{
public:
/**
* The caller is responsible that the geometry is available and unchanged
* for the whole lifetime of this object.
* @param geometry
*/
QgsInternalGeometryEngine( const QgsGeometry& geometry );

/**
* Will extrude a line or (segmentized) curve by a given offset and return a polygon
* representation of it.
*
* @param x offset in x direction
* @param y offset in y direction
* @return an extruded polygon
*/
QgsGeometry extrude( double x, double y );

private:
const QgsAbstractGeometryV2* mGeometry;
};

#endif // QGSINTERNALGEOMETRYENGINE_H
16 changes: 16 additions & 0 deletions src/core/qgsexpression.cpp
Expand Up @@ -2153,6 +2153,21 @@ static QVariant fcnAzimuth( const QVariantList& values, const QgsExpressionConte
return QVariant();
}

static QVariant fcnExtrude( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
if ( values.length() != 3 )
return QVariant();

QgsGeometry fGeom = getGeometry( values.at( 0 ), parent );
double x = getDoubleValue( values.at( 1 ), parent );
double y = getDoubleValue( values.at( 2 ), parent );

QgsGeometry geom = fGeom.extrude( x, y );

QVariant result = geom.geometry() ? QVariant::fromValue( geom ) : QVariant();
return result;
}

static QVariant fcnRound( const QVariantList& values, const QgsExpressionContext *, QgsExpression* parent )
{
if ( values.length() == 2 )
Expand Down Expand Up @@ -2915,6 +2930,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "geom_to_wkt", -1, fcnGeomToWKT, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomToWKT" )
<< new StaticFunction( "geometry", 1, fcnGetGeometry, "GeometryGroup", QString(), true )
<< new StaticFunction( "transform", 3, fcnTransformGeometry, "GeometryGroup" )
<< new StaticFunction( "extrude", 3, fcnExtrude, "GeometryGroup", QString() )
<< new StaticFunction( "$rownum", 0, fcnRowNumber, "deprecated" )
<< new StaticFunction( "$id", 0, fcnFeatureId, "Record" )
<< new StaticFunction( "$currentfeature", 0, fcnFeature, "Record" )
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsexpression.h
Expand Up @@ -280,7 +280,7 @@ class CORE_EXPORT QgsExpression
QgsDistanceArea* geomCalculator();

//! Sets the geometry calculator used in evaluation of expressions,
// instead of the default.
//! instead of the default.
void setGeomCalculator( const QgsDistanceArea &calc );

/** This function currently replaces each expression between [% and %]
Expand Down
13 changes: 12 additions & 1 deletion tests/src/python/test_qgsgeometry.py
Expand Up @@ -345,7 +345,7 @@ def testSimplifyIssue4189(self):
'simplify_error.wkt'), 'rt')
myWKT = myWKTFile.readline()
myWKTFile.close()
print myWKT
# print myWKT
myGeometry = QgsGeometry().fromWkt(myWKT)
assert myGeometry is not None
myStartLength = len(myWKT)
Expand Down Expand Up @@ -1191,6 +1191,17 @@ def testTranslate(self):
expwkt = "MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)),((4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0)))"
wkt = polygon.exportToWkt()

def testExtrude(self):
points = [QgsPoint(1, 2), QgsPoint(3, 2), QgsPoint(4, 3)]
line = QgsGeometry.fromPolyline(points)
expected = QgsGeometry.fromWkt('Polygon ((1 2, 3 2, 4 3, 5 5, 4 4, 2 4, 1 2))')
self.assertEqual(line.extrude(1, 2).exportToWkt(), expected.exportToWkt())

points2 = [[QgsPoint(1, 2), QgsPoint(3, 2)], [QgsPoint(4, 3), QgsPoint(8, 3)]]
multiline = QgsGeometry.fromMultiPolyline(points2)
expected = QgsGeometry.fromWkt('MultiPolygon (((1 2, 3 2, 4 4, 2 4, 1 2)),((4 3, 8 3, 9 5, 5 5, 4 3)))')
self.assertEqual(multiline.extrude(1, 2).exportToWkt(), expected.exportToWkt())

def testBoundingBox(self):
# 2-+-+-+-+-3
# | |
Expand Down

0 comments on commit 5425410

Please sign in to comment.