QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
qgsalgorithmbuffer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmbuffer.cpp
3 ---------------------
4 begin : April 2017
5 copyright : (C) 2017 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#include "qgsalgorithmbuffer.h"
19#include "qgsvectorlayer.h"
20#include "qgsmessagelog.h"
21
23
24QString QgsBufferAlgorithm::name() const
25{
26 return QStringLiteral( "buffer" );
27}
28
29QString QgsBufferAlgorithm::displayName() const
30{
31 return QObject::tr( "Buffer" );
32}
33
34QStringList QgsBufferAlgorithm::tags() const
35{
36 return QObject::tr( "buffer,grow,fixed,variable,distance" ).split( ',' );
37}
38
39QString QgsBufferAlgorithm::group() const
40{
41 return QObject::tr( "Vector geometry" );
42}
43
44QString QgsBufferAlgorithm::groupId() const
45{
46 return QStringLiteral( "vectorgeometry" );
47}
48
49void QgsBufferAlgorithm::initAlgorithm( const QVariantMap & )
50{
51 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
52
53 auto bufferParam = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "DISTANCE" ), QObject::tr( "Distance" ), 10, QStringLiteral( "INPUT" ) );
54 bufferParam->setIsDynamic( true );
55 bufferParam->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "Distance" ), QObject::tr( "Buffer distance" ), QgsPropertyDefinition::Double ) );
56 bufferParam->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
57 addParameter( bufferParam.release() );
58 auto segmentParam = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "SEGMENTS" ), QObject::tr( "Segments" ), Qgis::ProcessingNumberParameterType::Integer, 5, false, 1 );
59 segmentParam->setHelp( QObject::tr( "The segments parameter controls the number of line segments to use to approximate a quarter circle when creating rounded offsets." ) );
60 addParameter( segmentParam.release() );
61 addParameter( new QgsProcessingParameterEnum( QStringLiteral( "END_CAP_STYLE" ), QObject::tr( "End cap style" ), QStringList() << QObject::tr( "Round" ) << QObject::tr( "Flat" ) << QObject::tr( "Square" ), false, 0 ) );
62 addParameter( new QgsProcessingParameterEnum( QStringLiteral( "JOIN_STYLE" ), QObject::tr( "Join style" ), QStringList() << QObject::tr( "Round" ) << QObject::tr( "Miter" ) << QObject::tr( "Bevel" ), false, 0 ) );
63 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "MITER_LIMIT" ), QObject::tr( "Miter limit" ), Qgis::ProcessingNumberParameterType::Double, 2, false, 1 ) );
64
65 addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "DISSOLVE" ), QObject::tr( "Dissolve result" ), false ) );
66
67 auto keepDisjointParam = std::make_unique<QgsProcessingParameterBoolean>( QStringLiteral( "SEPARATE_DISJOINT" ), QObject::tr( "Keep disjoint results separate" ), false );
68 keepDisjointParam->setFlags( keepDisjointParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
69 keepDisjointParam->setHelp( QObject::tr( "If checked, then any disjoint parts in the buffer results will be output as separate single-part features." ) );
70 addParameter( keepDisjointParam.release() );
71
72 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Buffered" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), false, true, true ) );
73}
74
75QString QgsBufferAlgorithm::shortHelpString() const
76{
77 return QObject::tr( "This algorithm computes a buffer area for all the features in an input layer, using a fixed or dynamic distance.\n\n"
78 "The segments parameter controls the number of line segments to use to approximate a quarter circle when creating rounded offsets.\n\n"
79 "The end cap style parameter controls how line endings are handled in the buffer.\n\n"
80 "The join style parameter specifies whether round, miter or beveled joins should be used when offsetting corners in a line.\n\n"
81 "The miter limit parameter is only applicable for miter join styles, and controls the maximum distance from the offset curve to use when creating a mitered join." );
82}
83
84QString QgsBufferAlgorithm::shortDescription() const
85{
86 return QObject::tr( "Computes a buffer area for all the features in an input layer, using a fixed or dynamic distance." );
87}
88
89Qgis::ProcessingAlgorithmDocumentationFlags QgsBufferAlgorithm::documentationFlags() const
90{
92}
93
94QgsBufferAlgorithm *QgsBufferAlgorithm::createInstance() const
95{
96 return new QgsBufferAlgorithm();
97}
98
99QVariantMap QgsBufferAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
100{
101 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
102 if ( !source )
103 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
104
105 // fixed parameters
106 const bool dissolve = parameterAsBoolean( parameters, QStringLiteral( "DISSOLVE" ), context );
107 const bool keepDisjointSeparate = parameterAsBoolean( parameters, QStringLiteral( "SEPARATE_DISJOINT" ), context );
108 const int segments = parameterAsInt( parameters, QStringLiteral( "SEGMENTS" ), context );
109 const Qgis::EndCapStyle endCapStyle = static_cast<Qgis::EndCapStyle>( 1 + parameterAsInt( parameters, QStringLiteral( "END_CAP_STYLE" ), context ) );
110 const Qgis::JoinStyle joinStyle = static_cast<Qgis::JoinStyle>( 1 + parameterAsInt( parameters, QStringLiteral( "JOIN_STYLE" ), context ) );
111 const double miterLimit = parameterAsDouble( parameters, QStringLiteral( "MITER_LIMIT" ), context );
112 const double bufferDistance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
113 const bool dynamicBuffer = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
114
115 QString dest;
116 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), Qgis::WkbType::MultiPolygon, source->sourceCrs(), keepDisjointSeparate ? QgsFeatureSink::RegeneratePrimaryKey : QgsFeatureSink::SinkFlags() ) );
117 if ( !sink )
118 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
119
120 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() );
121 QgsProperty bufferProperty;
122 if ( dynamicBuffer )
123 {
124 bufferProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value<QgsProperty>();
125 }
126
127 const long count = source->featureCount();
128
129 QgsFeature f;
130 // buffer doesn't care about invalid features, and buffering can be used to repair geometries
132
133 const double step = count > 0 ? 100.0 / count : 1;
134 int current = 0;
135
136 QVector<QgsGeometry> bufferedGeometriesForDissolve;
137 QgsAttributes dissolveAttrs;
138
139 while ( it.nextFeature( f ) )
140 {
141 if ( feedback->isCanceled() )
142 {
143 break;
144 }
145 if ( dissolveAttrs.isEmpty() )
146 dissolveAttrs = f.attributes();
147
148 QgsFeature out = f;
149 if ( out.hasGeometry() )
150 {
151 double distance = bufferDistance;
152 if ( dynamicBuffer )
153 {
154 expressionContext.setFeature( f );
155 distance = bufferProperty.valueAsDouble( expressionContext, bufferDistance );
156 }
157
158 QgsGeometry outputGeometry = f.geometry().buffer( distance, segments, endCapStyle, joinStyle, miterLimit );
159 if ( outputGeometry.isNull() )
160 {
161 QgsMessageLog::logMessage( QObject::tr( "Error calculating buffer for feature %1" ).arg( f.id() ), QObject::tr( "Processing" ), Qgis::MessageLevel::Warning );
162 }
163 if ( dissolve )
164 {
165 bufferedGeometriesForDissolve << outputGeometry;
166 }
167 else
168 {
169 outputGeometry.convertToMultiType();
170
171 if ( !keepDisjointSeparate )
172 {
173 out.setGeometry( outputGeometry );
174
175 if ( !sink->addFeature( out, QgsFeatureSink::FastInsert ) )
176 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
177 }
178 else
179 {
180 for ( auto partIt = outputGeometry.const_parts_begin(); partIt != outputGeometry.const_parts_end(); ++partIt )
181 {
182 if ( const QgsAbstractGeometry *part = *partIt )
183 {
184 out.setGeometry( QgsGeometry( part->clone() ) );
185 if ( !sink->addFeature( out, QgsFeatureSink::FastInsert ) )
186 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
187 }
188 }
189 }
190 }
191 }
192 else if ( !dissolve )
193 {
194 if ( !sink->addFeature( out, QgsFeatureSink::FastInsert ) )
195 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
196 }
197
198 feedback->setProgress( current * step );
199 current++;
200 }
201
202 if ( dissolve && !bufferedGeometriesForDissolve.isEmpty() )
203 {
204 QgsGeometry finalGeometry = QgsGeometry::unaryUnion( bufferedGeometriesForDissolve );
205 finalGeometry.convertToMultiType();
206 QgsFeature f;
207 f.setAttributes( dissolveAttrs );
208
209 if ( !keepDisjointSeparate )
210 {
211 f.setGeometry( finalGeometry );
212 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
213 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
214 }
215 else
216 {
217 for ( auto partIt = finalGeometry.const_parts_begin(); partIt != finalGeometry.const_parts_end(); ++partIt )
218 {
219 if ( const QgsAbstractGeometry *part = *partIt )
220 {
221 f.setGeometry( QgsGeometry( part->clone() ) );
222 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
223 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
224 }
225 }
226 }
227 }
228
229 sink->finalize();
230
231 QVariantMap outputs;
232 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
233 return outputs;
234}
235
236Qgis::ProcessingAlgorithmFlags QgsBufferAlgorithm::flags() const
237{
240 return f;
241}
242
243QgsProcessingAlgorithm::VectorProperties QgsBufferAlgorithm::sinkProperties( const QString &sink, const QVariantMap &parameters, QgsProcessingContext &context, const QMap<QString, QgsProcessingAlgorithm::VectorProperties> &sourceProperties ) const
244{
245 const bool keepDisjointSeparate = parameterAsBoolean( parameters, QStringLiteral( "SEPARATE_DISJOINT" ), context );
246
248 if ( sink == QLatin1String( "OUTPUT" ) )
249 {
250 if ( sourceProperties.value( QStringLiteral( "INPUT" ) ).availability == Qgis::ProcessingPropertyAvailability::Available )
251 {
252 const VectorProperties inputProps = sourceProperties.value( QStringLiteral( "INPUT" ) );
253 result.fields = inputProps.fields;
254 result.crs = inputProps.crs;
255 result.wkbType = keepDisjointSeparate ? Qgis::WkbType::Polygon : Qgis::WkbType::MultiPolygon;
257 return result;
258 }
259 else
260 {
261 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
262 if ( source )
263 {
264 result.fields = source->fields();
265 result.crs = source->sourceCrs();
266 result.wkbType = keepDisjointSeparate ? Qgis::WkbType::Polygon : Qgis::WkbType::MultiPolygon;
268 return result;
269 }
270 }
271 }
272 return result;
273}
274
275bool QgsBufferAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
276{
277 const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( layer );
278 if ( !vlayer )
279 return false;
280 //Only Polygons
281 return vlayer->wkbType() == Qgis::WkbType::Polygon || vlayer->wkbType() == Qgis::WkbType::MultiPolygon;
282}
283
@ VectorPolygon
Vector polygon layers.
@ Warning
Warning message.
Definition qgis.h:156
@ Available
Properties are available.
JoinStyle
Join styles for buffers.
Definition qgis.h:2084
@ RegeneratesPrimaryKeyInSomeScenarios
Algorithm may drop the existing primary keys or FID values in some scenarios, depending on algorithm ...
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3476
EndCapStyle
End cap styles for buffers.
Definition qgis.h:2071
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3496
@ SkipGeometryValidityChecks
Invalid geometry checks should always be skipped. This flag can be useful for algorithms which always...
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
@ SupportsInPlaceEdits
Algorithm supports in-place editing.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Abstract base class for all geometries.
A vector of attributes.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
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.
Wraps a request for features to a vector layer (or directly its vector data provider).
An interface for objects which accept features via addFeature(s) methods.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
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
A geometry is the spatial representation of a feature.
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
Base class for all map layer types.
Definition qgsmaplayer.h:77
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
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.
A boolean parameter for processing algorithms.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A numeric parameter for processing algorithms.
static bool isDynamic(const QVariantMap &parameters, const QString &name)
Returns true if the parameter with matching name is a dynamic parameter, and must be evaluated once f...
Definition for a property.
Definition qgsproperty.h:45
@ Double
Double value (including negative values)
Definition qgsproperty.h:55
A store for object properties.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
double valueAsDouble(const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a double.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
Properties of a vector source or sink used in an algorithm.
Qgis::WkbType wkbType
Geometry (WKB) type.
QgsCoordinateReferenceSystem crs
Coordinate Reference System.
Qgis::ProcessingPropertyAvailability availability
Availability of the properties. By default properties are not available.