QGIS API Documentation 3.38.0-Grenoble (exported)
Loading...
Searching...
No Matches
qgsalgorithmcoveragesimplify.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmcoveragesimplify.cpp
3 ---------------------
4 begin : October 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18
21#include "qgsgeos.h"
22
23
25
26QString QgsCoverageSimplifyAlgorithm::name() const
27{
28 return QStringLiteral( "coveragesimplify" );
29}
30
31QString QgsCoverageSimplifyAlgorithm::displayName() const
32{
33 return QObject::tr( "Simplify coverage" );
34}
35
36QStringList QgsCoverageSimplifyAlgorithm::tags() const
37{
38 return QObject::tr( "topological,boundary" ).split( ',' );
39}
40
41QString QgsCoverageSimplifyAlgorithm::group() const
42{
43 return QObject::tr( "Vector coverage" );
44}
45
46QString QgsCoverageSimplifyAlgorithm::groupId() const
47{
48 return QStringLiteral( "vectorcoverage" );
49}
50
51void QgsCoverageSimplifyAlgorithm::initAlgorithm( const QVariantMap & )
52{
53 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList< int >() << static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon ) ) );
54 addParameter( new QgsProcessingParameterDistance( QStringLiteral( "TOLERANCE" ),
55 QObject::tr( "Tolerance" ), 1.0, QStringLiteral( "INPUT" ), false, 0, 10000000.0 ) );
56 std::unique_ptr< QgsProcessingParameterBoolean > boundaryParameter = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "PRESERVE_BOUNDARY" ), QObject::tr( "Preserve boundary" ), false );
57 boundaryParameter->setHelp( QObject::tr( "When enabled the outside edges of the coverage will be preserved without simplification." ) );
58 addParameter( boundaryParameter.release() );
59
60 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Simplified" ), Qgis::ProcessingSourceType::VectorPolygon ) );
61}
62
63QString QgsCoverageSimplifyAlgorithm::shortDescription() const
64{
65 return QObject::tr( "Simplifies a coverage of polygon features while retaining valid coverage" );
66}
67
68QString QgsCoverageSimplifyAlgorithm::shortHelpString() const
69{
70 return QObject::tr( "This algorithm operates on a coverage (represented as a set of polygon features "
71 "with exactly matching edge geometry) to apply a Visvalingam–Whyatt "
72 "simplification to the edges, reducing complexity in proportion with "
73 "the provided tolerance, while retaining a valid coverage (i.e. no edges "
74 "will cross or touch after the simplification).\n\n"
75 "Geometries will never be removed, but they may be simplified down to just "
76 "a triangle. Also, some geometries (such as polygons which have too "
77 "few non-repeated points) will be returned unchanged.\n\n"
78 "If the input dataset is not a valid coverage due to overlaps, "
79 "it will still be simplified, but invalid topology such as crossing "
80 "edges will still be invalid." );
81}
82
83QgsCoverageSimplifyAlgorithm *QgsCoverageSimplifyAlgorithm::createInstance() const
84{
85 return new QgsCoverageSimplifyAlgorithm();
86}
87
88QVariantMap QgsCoverageSimplifyAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
89{
90 std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
91 if ( !source )
92 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
93
94 const bool preserveBoundary = parameterAsBoolean( parameters, QStringLiteral( "PRESERVE_BOUNDARY" ), context );
95 const double tolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context );
96
97 QString sinkId;
98 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, sinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
99 if ( !sink )
100 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
101
102 // we have no choice but to build up a list of features in advance
103 QVector< QgsFeature > featuresWithGeom;
104 QVector< QgsFeature > featuresWithoutGeom;
105 QgsGeometryCollection collection;
106
107 const long count = source->featureCount();
108 if ( count > 0 )
109 {
110 featuresWithGeom.reserve( count );
111 collection.reserve( count );
112 }
113
114 const double step = count > 0 ? 100.0 / count : 1;
115 int current = 0;
116
117 feedback->pushInfo( QObject::tr( "Collecting features" ) );
118
119 QgsFeature inFeature;
120 QgsFeatureIterator features = source->getFeatures();
121 while ( features.nextFeature( inFeature ) )
122 {
123 if ( feedback->isCanceled() )
124 {
125 break;
126 }
127
128 if ( inFeature.hasGeometry() )
129 {
130 featuresWithGeom.append( inFeature );
131 collection.addGeometry( inFeature.geometry().constGet()->clone() );
132 }
133 else
134 {
135 featuresWithoutGeom.append( inFeature );
136 }
137
138
139 feedback->setProgress( current * step * 0.2 );
140 current++;
141 }
142
143 QString error;
144 QgsGeos geos( &collection );
145 switch ( source->invalidGeometryCheck() )
146 {
148 break;
149
152 {
153 if ( geos.validateCoverage( 0, nullptr, &error ) != Qgis::CoverageValidityResult::Valid )
154 {
155 throw QgsProcessingException( QObject::tr( "Coverage is not valid" ) );
156 }
157 break;
158 }
159 }
160
161 feedback->pushInfo( QObject::tr( "Simplifying coverage" ) );
162
163 std::unique_ptr< QgsAbstractGeometry > simplified;
164 try
165 {
166 simplified = geos.simplifyCoverageVW( tolerance, preserveBoundary, &error );
167 }
168 catch ( QgsNotSupportedException &e )
169 {
170 throw QgsProcessingException( e.what() );
171 }
172
173 if ( !simplified )
174 {
175 if ( !error.isEmpty() )
176 throw QgsProcessingException( error );
177 else
178 throw QgsProcessingException( QObject::tr( "No geometry was returned for simplified coverage" ) );
179 }
180
181 feedback->setProgress( 80 );
182
183 feedback->pushInfo( QObject::tr( "Storing features" ) );
184 long long featureIndex = 0;
185 for ( auto partsIt = simplified->const_parts_begin(); partsIt != simplified->const_parts_end(); ++partsIt )
186 {
187 QgsFeature outFeature = featuresWithGeom.value( featureIndex );
188 outFeature.setGeometry( QgsGeometry( *partsIt ? ( *partsIt )->clone() : nullptr ) );
189 if ( !sink->addFeature( outFeature, QgsFeatureSink::FastInsert ) )
190 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
191
192
193 feedback->setProgress( featureIndex * step * 0.2 + 80 );
194 featureIndex++;
195 }
196 for ( const QgsFeature &feature : featuresWithoutGeom )
197 {
198 QgsFeature outFeature = feature;
199 if ( !sink->addFeature( outFeature, QgsFeatureSink::FastInsert ) )
200 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
201 }
202
203 QVariantMap outputs;
204 outputs.insert( QStringLiteral( "OUTPUT" ), sinkId );
205 return outputs;
206}
207
@ VectorPolygon
Vector polygon layers.
@ Valid
Coverage is valid.
@ NoCheck
No invalid geometry checking.
@ AbortOnInvalid
Close iterator on encountering any features with invalid geometry. This requires a slow geometry vali...
@ SkipInvalid
Skip any features with invalid geometry. This requires a slow geometry validity check for every featu...
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QString what() const
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
void reserve(int size)
Attempts to allocate memory for at least size geometries.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition qgsgeos.h:137
Custom exception class which is raised when an operation is not supported.
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
A double numeric parameter for distance values.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
Contains geos related utilities and functions.
Definition qgsgeos.h:75