diff --git a/python/plugins/processing/tests/testdata/expected/force_rhr_multipolys.gml b/python/plugins/processing/tests/testdata/expected/force_rhr_multipolys.gml new file mode 100644 index 00000000000..b642dd7e631 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/force_rhr_multipolys.gml @@ -0,0 +1,45 @@ + + + + + 0-1 + 96 + + + + + + 2,1 2,2 3,2 3,3 4,3 4,1 2,1 + Test + 1 + 0.123 + + + + + 7,-1 7,3 8,3 8,-1 7,-17,6 9,6 9,5 8,4 7,4 7,5 7,6 + + + + + + + + 0,0 0,1 1,1 1,0 0,0 + Test + 2 + -0.123 + + + + + Test + 3 + 0 + + + diff --git a/python/plugins/processing/tests/testdata/expected/force_rhr_multipolys.xsd b/python/plugins/processing/tests/testdata/expected/force_rhr_multipolys.xsd new file mode 100644 index 00000000000..f34f32ca633 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/force_rhr_multipolys.xsd @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/plugins/processing/tests/testdata/expected/force_rhr_polys.gml b/python/plugins/processing/tests/testdata/expected/force_rhr_polys.gml new file mode 100644 index 00000000000..00f771189ec --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/force_rhr_polys.gml @@ -0,0 +1,61 @@ + + + + + -1-3 + 106 + + + + + + -1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1 + aaaaa + 33 + 44.123456 + + + + + 5,5 6,4 4,4 5,5 + Aaaaa + -33 + 0 + + + + + 2,5 2,6 3,6 3,5 2,5 + bbaaa + + 0.123 + + + + + 6,1 10,1 10,-3 6,-3 6,17,0 7,-2 9,-2 9,0 7,0 + ASDF + 0 + + + + + + + 120 + -100291.43213 + + + + + 3,2 6,1 6,-3 2,-1 2,2 3,2 + elim + 2 + 3.33 + + + diff --git a/python/plugins/processing/tests/testdata/expected/force_rhr_polys.xsd b/python/plugins/processing/tests/testdata/expected/force_rhr_polys.xsd new file mode 100644 index 00000000000..489096a0444 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/force_rhr_polys.xsd @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 9bae1383da6..e0b51c3dd83 100755 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -6325,4 +6325,26 @@ tests: name: expected/remove_duplicates3.gml type: vector + - algorithm: native:forcerhr + name: Force right-hand-rule polys + params: + INPUT: + name: polys.gml|layername=polys2 + type: vector + results: + OUTPUT: + name: expected/force_rhr_polys.gml + type: vector + + - algorithm: native:forcerhr + name: Force right-hand-rule multipolys + params: + INPUT: + name: multipolys.gml|layername=multipolys + type: vector + results: + OUTPUT: + name: expected/force_rhr_multipolys.gml + type: vector + # See ../README.md for a description of the file format diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt index 8d2a9342030..138656c9847 100644 --- a/src/analysis/CMakeLists.txt +++ b/src/analysis/CMakeLists.txt @@ -50,6 +50,7 @@ SET(QGIS_ANALYSIS_SRCS processing/qgsalgorithmfilter.cpp processing/qgsalgorithmfiltervertices.cpp processing/qgsalgorithmfixgeometries.cpp + processing/qgsalgorithmforcerhr.cpp processing/qgsalgorithmimportphotos.cpp processing/qgsalgorithminterpolatepoint.cpp processing/qgsalgorithmintersection.cpp diff --git a/src/analysis/processing/qgsalgorithmforcerhr.cpp b/src/analysis/processing/qgsalgorithmforcerhr.cpp new file mode 100644 index 00000000000..ca86d29a3f1 --- /dev/null +++ b/src/analysis/processing/qgsalgorithmforcerhr.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + qgsalgorithmforcerhr.cpp + --------------------- + begin : November 2018 + copyright : (C) 2018 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 "qgsalgorithmforcerhr.h" +#include "qgsvectorlayer.h" +#include "qgsgeometrycollection.h" +#include "qgscurvepolygon.h" + +///@cond PRIVATE + +QString QgsForceRHRAlgorithm::name() const +{ + return QStringLiteral( "forcerhr" ); +} + +QString QgsForceRHRAlgorithm::displayName() const +{ + return QObject::tr( "Force right-hand-rule" ); +} + +QStringList QgsForceRHRAlgorithm::tags() const +{ + return QObject::tr( "clockwise,counter,orientation,ring,repair,invalid,geometry,make,valid" ).split( ',' ); +} + +QString QgsForceRHRAlgorithm::group() const +{ + return QObject::tr( "Vector geometry" ); +} + +QString QgsForceRHRAlgorithm::groupId() const +{ + return QStringLiteral( "vectorgeometry" ); +} + +QgsProcessingFeatureSource::Flag QgsForceRHRAlgorithm::sourceFlags() const +{ + return QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks; +} + +QString QgsForceRHRAlgorithm::outputName() const +{ + return QObject::tr( "Reoriented" ); +} + +QString QgsForceRHRAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm forces polygon geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon " + "is to the right of the boundary. In particular, the exterior ring is oriented in a clockwise direction and the interior " + "rings in a counter-clockwise direction." ); +} + +QString QgsForceRHRAlgorithm::shortDescription() const +{ + return QObject::tr( "Forces polygon geometries to respect the Right-Hand-Rule." ); +} + +QList QgsForceRHRAlgorithm::inputLayerTypes() const +{ + return QList() << QgsProcessing::TypeVectorPolygon; +} + +QgsForceRHRAlgorithm *QgsForceRHRAlgorithm::createInstance() const +{ + return new QgsForceRHRAlgorithm(); +} + +QgsFeatureList QgsForceRHRAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * ) +{ + if ( !feature.hasGeometry() ) + return QgsFeatureList() << feature; + + const QgsGeometry inputGeom = feature.geometry(); + QgsGeometry outputGeometry = inputGeom; + if ( inputGeom.isMultipart() ) + { + const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( inputGeom.constGet() ); + std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() ); + for ( int i = 0; i < collection->numGeometries(); ++i ) + { + const QgsAbstractGeometry *g = collection->geometryN( i ); + if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) ) + { + std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() ); + corrected->forceRHR(); + newCollection->addGeometry( corrected.release() ); + } + else + { + newCollection->addGeometry( g->clone() ); + } + } + outputGeometry = QgsGeometry( std::move( newCollection ) ); + } + else + { + if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( inputGeom.constGet() ) ) + { + std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() ); + corrected->forceRHR(); + outputGeometry = QgsGeometry( std::move( corrected ) ); + } + } + + QgsFeature outputFeature = feature; + outputFeature.setGeometry( outputGeometry ); + + return QgsFeatureList() << outputFeature; +} + +///@endcond diff --git a/src/analysis/processing/qgsalgorithmforcerhr.h b/src/analysis/processing/qgsalgorithmforcerhr.h new file mode 100644 index 00000000000..8f58bdd9ddd --- /dev/null +++ b/src/analysis/processing/qgsalgorithmforcerhr.h @@ -0,0 +1,58 @@ +/*************************************************************************** + qgsalgorithmforcerhr.h + --------------------- + begin : November 2018 + copyright : (C) 2018 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 QGSALGORITHMFORCERHR_H +#define QGSALGORITHMFORCERHR_H + +#define SIP_NO_FILE + +#include "qgis.h" +#include "qgsprocessingalgorithm.h" + +///@cond PRIVATE + +/** + * Native force right-hand-rule algorithm. + */ +class QgsForceRHRAlgorithm : public QgsProcessingFeatureBasedAlgorithm +{ + + public: + + QgsForceRHRAlgorithm() = default; + QString name() const override; + QString displayName() const override; + QStringList tags() const override; + QString group() const override; + QString groupId() const override; + QString shortHelpString() const override; + QString shortDescription() const override; + QList inputLayerTypes() const override; + QgsForceRHRAlgorithm *createInstance() const override SIP_FACTORY; + + protected: + QgsProcessingFeatureSource::Flag sourceFlags() const override; + QString outputName() const override; + QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; + +}; + +///@endcond PRIVATE + +#endif // QGSALGORITHMFORCERHR_H + + diff --git a/src/analysis/processing/qgsnativealgorithms.cpp b/src/analysis/processing/qgsnativealgorithms.cpp index db86d96ff41..82b36142095 100644 --- a/src/analysis/processing/qgsnativealgorithms.cpp +++ b/src/analysis/processing/qgsnativealgorithms.cpp @@ -45,6 +45,7 @@ #include "qgsalgorithmfilter.h" #include "qgsalgorithmfiltervertices.h" #include "qgsalgorithmfixgeometries.h" +#include "qgsalgorithmforcerhr.h" #include "qgsalgorithmjoinbyattribute.h" #include "qgsalgorithmjoinwithlines.h" #include "qgsalgorithmimportphotos.h" @@ -172,6 +173,7 @@ void QgsNativeAlgorithms::loadAlgorithms() addAlgorithm( new QgsFilterVerticesByM() ); addAlgorithm( new QgsFilterVerticesByZ() ); addAlgorithm( new QgsFixGeometriesAlgorithm() ); + addAlgorithm( new QgsForceRHRAlgorithm() ); addAlgorithm( new QgsImportPhotosAlgorithm() ); addAlgorithm( new QgsInterpolatePointAlgorithm() ); addAlgorithm( new QgsIntersectionAlgorithm() );