forked from qgis/QGIS
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Processing: Add Check Geometry algorithm and test: Self intersection
- Loading branch information
Showing
5 changed files
with
306 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
203 changes: 203 additions & 0 deletions
203
src/analysis/processing/qgsalgorithmcheckgeometryselfintersection.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
/*************************************************************************** | ||
qgsalgorithmcheckgeometryselfintersection.cpp | ||
--------------------- | ||
begin : January 2024 | ||
copyright : (C) 2024 by Jacky Volpes | ||
email : jacky dot volpes at oslandia 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 "qgsalgorithmcheckgeometryselfintersection.h" | ||
#include "qgsgeometrycheckcontext.h" | ||
#include "qgsgeometrycheckerror.h" | ||
#include "qgsgeometryselfintersectioncheck.h" | ||
#include "qgspoint.h" | ||
#include "qgsvectorlayer.h" | ||
#include "qgsvectordataproviderfeaturepool.h" | ||
|
||
///@cond PRIVATE | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::name() const -> QString | ||
{ | ||
return QStringLiteral( "checkgeometryselfintersection" ); | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::displayName() const -> QString | ||
{ | ||
return QObject::tr( "Check Geometry (Self intersection)" ); | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::tags() const -> QStringList | ||
{ | ||
return QObject::tr( "check,geometry,intersection" ).split( ',' ); | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::group() const -> QString | ||
{ | ||
return QObject::tr( "Check geometry" ); | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::groupId() const -> QString | ||
{ | ||
return QStringLiteral( "checkgeometry" ); | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::shortHelpString() const -> QString | ||
{ | ||
return QObject::tr( "This algorithm check the self intersections of geometry." ); | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::flags() const -> QgsProcessingAlgorithm::Flags | ||
{ | ||
return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagNoThreading; | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::createInstance() const -> QgsGeometryCheckSelfIntersectionAlgorithm * | ||
{ | ||
return new QgsGeometryCheckSelfIntersectionAlgorithm(); | ||
} | ||
|
||
void QgsGeometryCheckSelfIntersectionAlgorithm::initAlgorithm( const QVariantMap &configuration ) | ||
{ | ||
Q_UNUSED( configuration ) | ||
|
||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorPolygon << QgsProcessing::TypeVectorLine ) ); | ||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "ERRORS" ), QObject::tr( "Errors layer" ), QgsProcessing::TypeVectorPoint ) ); | ||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output layer" ), QgsProcessing::TypeVectorAnyGeometry ) ); | ||
|
||
std::unique_ptr< QgsProcessingParameterNumber > tolerance = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "TOLERANCE" ), | ||
QObject::tr( "Tolerance" ), QgsProcessingParameterNumber::Integer, 8, false, 1, 13 ); | ||
tolerance->setFlags( tolerance->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); | ||
addParameter( tolerance.release() ); | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * ) -> bool | ||
{ | ||
mTolerance = parameterAsInt( parameters, QStringLiteral( "TOLERANCE" ), context ); | ||
|
||
return true; | ||
} | ||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::createFeaturePool( QgsVectorLayer *layer, bool selectedOnly ) const -> QgsFeaturePool * | ||
{ | ||
return new QgsVectorDataProviderFeaturePool( layer, selectedOnly ); | ||
} | ||
|
||
static auto outputFields( ) -> QgsFields | ||
{ | ||
QgsFields fields; | ||
fields.append( QgsField( QStringLiteral( "gc_layerid" ), QVariant::String ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_layername" ), QVariant::String ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_featid" ), QVariant::Int ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_partidx" ), QVariant::Int ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_ringidx" ), QVariant::Int ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_vertidx" ), QVariant::Int ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_errorx" ), QVariant::Double ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_errory" ), QVariant::Double ) ); | ||
fields.append( QgsField( QStringLiteral( "gc_error" ), QVariant::String ) ); | ||
return fields; | ||
} | ||
|
||
|
||
auto QgsGeometryCheckSelfIntersectionAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) -> QVariantMap | ||
{ | ||
QString dest_output; | ||
QString dest_errors; | ||
std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); | ||
if ( !source ) | ||
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); | ||
|
||
mInputLayer.reset( source->materialize( QgsFeatureRequest() ) ); | ||
|
||
if ( !mInputLayer.get() ) | ||
throw QgsProcessingException( QObject::tr( "Could not load source layer for INPUT" ) ); | ||
|
||
QgsFields fields = outputFields(); | ||
|
||
std::unique_ptr< QgsFeatureSink > sink_output( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest_output, fields, mInputLayer->wkbType(), mInputLayer->sourceCrs() ) ); | ||
if ( !sink_output ) | ||
{ | ||
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); | ||
} | ||
std::unique_ptr< QgsFeatureSink > sink_errors( parameterAsSink( parameters, QStringLiteral( "ERRORS" ), context, dest_errors, fields, Qgis::WkbType::Point, mInputLayer->sourceCrs() ) ); | ||
if ( !sink_errors ) | ||
{ | ||
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "ERRORS" ) ) ); | ||
} | ||
|
||
QgsProcessingMultiStepFeedback multiStepFeedback( 3, feedback ); | ||
|
||
QgsProject *project = mInputLayer->project() ? mInputLayer->project() : QgsProject::instance(); | ||
|
||
std::unique_ptr<QgsGeometryCheckContext> checkContext = std::make_unique<QgsGeometryCheckContext>( mTolerance, mInputLayer->sourceCrs(), project->transformContext(), project ); | ||
|
||
// Test detection | ||
QList<QgsGeometryCheckError *> checkErrors; | ||
QStringList messages; | ||
|
||
const QgsGeometrySelfIntersectionCheck check( checkContext.get(), QVariantMap() ); | ||
|
||
multiStepFeedback.setCurrentStep( 1 ); | ||
feedback->setProgressText( QObject::tr( "Preparing features…" ) ); | ||
QMap<QString, QgsFeaturePool *> featurePools; | ||
featurePools.insert( mInputLayer->id(), createFeaturePool( mInputLayer.get() ) ); | ||
|
||
multiStepFeedback.setCurrentStep( 2 ); | ||
feedback->setProgressText( QObject::tr( "Collecting errors…" ) ); | ||
check.collectErrors( featurePools, checkErrors, messages, feedback ); | ||
|
||
multiStepFeedback.setCurrentStep( 3 ); | ||
feedback->setProgressText( QObject::tr( "Exporting errors…" ) ); | ||
double step{checkErrors.size() > 0 ? 100.0 / checkErrors.size() : 1}; | ||
long i = 0; | ||
feedback->setProgress( 0.0 ); | ||
|
||
|
||
for ( QgsGeometryCheckError *error : checkErrors ) | ||
{ | ||
|
||
if ( feedback->isCanceled() ) | ||
break; | ||
|
||
QgsFeature f; | ||
QgsAttributes attrs = f.attributes(); | ||
|
||
attrs << error->layerId() | ||
<< mInputLayer->name() | ||
<< error->featureId() | ||
<< error->vidx().part | ||
<< error->vidx().ring | ||
<< error->vidx().vertex | ||
<< error->location().x() | ||
<< error->location().y() | ||
<< error->value().toString(); | ||
f.setAttributes( attrs ); | ||
|
||
f.setGeometry( error->geometry() ); | ||
if ( !sink_output->addFeature( f, QgsFeatureSink::FastInsert ) ) | ||
throw QgsProcessingException( writeFeatureError( sink_output.get(), parameters, QStringLiteral( "OUTPUT" ) ) ); | ||
|
||
f.setGeometry( QgsGeometry::fromPoint( QgsPoint( error->location().x(), error->location().y() ) ) ); | ||
if ( !sink_errors->addFeature( f, QgsFeatureSink::FastInsert ) ) | ||
throw QgsProcessingException( writeFeatureError( sink_errors.get(), parameters, QStringLiteral( "ERRORS" ) ) ); | ||
|
||
i++; | ||
feedback->setProgress( 100.0 * step * static_cast<double>( i ) ); | ||
} | ||
|
||
QVariantMap outputs; | ||
outputs.insert( QStringLiteral( "OUTPUT" ), dest_output ); | ||
outputs.insert( QStringLiteral( "ERRORS" ), dest_errors ); | ||
|
||
return outputs; | ||
} | ||
|
||
///@endcond |
59 changes: 59 additions & 0 deletions
59
src/analysis/processing/qgsalgorithmcheckgeometryselfintersection.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/*************************************************************************** | ||
qgsalgorithmcheckgeometryselfintersection.h | ||
--------------------- | ||
begin : January 2024 | ||
copyright : (C) 2024 by Jacky Volpes | ||
email : jacky dot volpes at oslandia 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 QGSALGORITHMCHECKGEOMETRYSELFINTERSECTION_H | ||
#define QGSALGORITHMCHECKGEOMETRYSELFINTERSECTION_H | ||
|
||
#define SIP_NO_FILE | ||
|
||
#include "qgis_sip.h" | ||
#include "qgsprocessingalgorithm.h" | ||
#include "qgsfeaturepool.h" | ||
|
||
///@cond PRIVATE | ||
|
||
class QgsGeometryCheckSelfIntersectionAlgorithm : public QgsProcessingAlgorithm | ||
{ | ||
public: | ||
|
||
QgsGeometryCheckSelfIntersectionAlgorithm() = default; | ||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override; | ||
QString name() const override; | ||
QString displayName() const override; | ||
QStringList tags() const override; | ||
QString group() const override; | ||
QString groupId() const override; | ||
QString shortHelpString() const override; | ||
Flags flags() const override; | ||
QgsGeometryCheckSelfIntersectionAlgorithm *createInstance() const override SIP_FACTORY; | ||
|
||
protected: | ||
|
||
bool prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; | ||
QVariantMap processAlgorithm( const QVariantMap ¶meters, | ||
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; | ||
|
||
private: | ||
QgsFeaturePool *createFeaturePool( QgsVectorLayer *layer, bool selectedOnly = false ) const; | ||
|
||
std::unique_ptr< QgsVectorLayer > mInputLayer; | ||
int mTolerance{8}; | ||
}; | ||
|
||
///@endcond PRIVATE | ||
|
||
#endif // QGSALGORITHMCHECKGEOMETRYSELFINTERSECTION_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters