Add extrude function to QgsGeometry

This commit is contained in:
Matthias Kuhn 2015-12-21 20:58:57 +01:00
parent e290c985f7
commit 542541007c
11 changed files with 212 additions and 7 deletions

View File

@ -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

View File

@ -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)))"
}
]
}

View File

@ -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
@ -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

View File

@ -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"
@ -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;

View File

@ -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

View File

@ -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"
@ -106,4 +106,4 @@ class CORE_EXPORT QgsGeometryEngine
QgsGeometryEngine();
};
#endif // QGSVECTORTOPOLOGY_H
#endif // QGSGEOMETRYENGINE_H

View File

@ -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();
}

View File

@ -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

View File

@ -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 )
@ -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" )

View File

@ -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 %]

View File

@ -345,7 +345,7 @@ class TestQgsGeometry(TestCase):
'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)
@ -1191,6 +1191,17 @@ class TestQgsGeometry(TestCase):
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
# | |