QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
84QgsBufferAlgorithm *QgsBufferAlgorithm::createInstance() const
85{
86 return new QgsBufferAlgorithm();
87}
88
89QVariantMap QgsBufferAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
90{
91 std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
92 if ( !source )
93 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
94
95 QString dest;
96 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), Qgis::WkbType::MultiPolygon, source->sourceCrs() ) );
97 if ( !sink )
98 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
99
100 // fixed parameters
101 const bool dissolve = parameterAsBoolean( parameters, QStringLiteral( "DISSOLVE" ), context );
102 const bool keepDisjointSeparate = parameterAsBoolean( parameters, QStringLiteral( "SEPARATE_DISJOINT" ), context );
103 const int segments = parameterAsInt( parameters, QStringLiteral( "SEGMENTS" ), context );
104 const Qgis::EndCapStyle endCapStyle = static_cast< Qgis::EndCapStyle >( 1 + parameterAsInt( parameters, QStringLiteral( "END_CAP_STYLE" ), context ) );
105 const Qgis::JoinStyle joinStyle = static_cast< Qgis::JoinStyle>( 1 + parameterAsInt( parameters, QStringLiteral( "JOIN_STYLE" ), context ) );
106 const double miterLimit = parameterAsDouble( parameters, QStringLiteral( "MITER_LIMIT" ), context );
107 const double bufferDistance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
108 const bool dynamicBuffer = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
109 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() );
110 QgsProperty bufferProperty;
111 if ( dynamicBuffer )
112 {
113 bufferProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value< QgsProperty >();
114 }
115
116 const long count = source->featureCount();
117
118 QgsFeature f;
119 // buffer doesn't care about invalid features, and buffering can be used to repair geometries
121
122 const double step = count > 0 ? 100.0 / count : 1;
123 int current = 0;
124
125 QVector< QgsGeometry > bufferedGeometriesForDissolve;
126 QgsAttributes dissolveAttrs;
127
128 while ( it.nextFeature( f ) )
129 {
130 if ( feedback->isCanceled() )
131 {
132 break;
133 }
134 if ( dissolveAttrs.isEmpty() )
135 dissolveAttrs = f.attributes();
136
137 QgsFeature out = f;
138 if ( out.hasGeometry() )
139 {
140 double distance = bufferDistance;
141 if ( dynamicBuffer )
142 {
143 expressionContext.setFeature( f );
144 distance = bufferProperty.valueAsDouble( expressionContext, bufferDistance );
145 }
146
147 QgsGeometry outputGeometry = f.geometry().buffer( distance, segments, endCapStyle, joinStyle, miterLimit );
148 if ( outputGeometry.isNull() )
149 {
150 QgsMessageLog::logMessage( QObject::tr( "Error calculating buffer for feature %1" ).arg( f.id() ), QObject::tr( "Processing" ), Qgis::MessageLevel::Warning );
151 }
152 if ( dissolve )
153 {
154 bufferedGeometriesForDissolve << outputGeometry;
155 }
156 else
157 {
158 outputGeometry.convertToMultiType();
159
160 if ( !keepDisjointSeparate )
161 {
162 out.setGeometry( outputGeometry );
163
164 if ( !sink->addFeature( out, QgsFeatureSink::FastInsert ) )
165 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
166 }
167 else
168 {
169 for ( auto partIt = outputGeometry.const_parts_begin(); partIt != outputGeometry.const_parts_end(); ++partIt )
170 {
171 if ( const QgsAbstractGeometry *part = *partIt )
172 {
173 out.setGeometry( QgsGeometry( part->clone() ) );
174 if ( !sink->addFeature( out, QgsFeatureSink::FastInsert ) )
175 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
176 }
177 }
178 }
179 }
180 }
181 else if ( !dissolve )
182 {
183 if ( !sink->addFeature( out, QgsFeatureSink::FastInsert ) )
184 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
185 }
186
187 feedback->setProgress( current * step );
188 current++;
189 }
190
191 if ( dissolve && !bufferedGeometriesForDissolve.isEmpty() )
192 {
193 QgsGeometry finalGeometry = QgsGeometry::unaryUnion( bufferedGeometriesForDissolve );
194 finalGeometry.convertToMultiType();
195 QgsFeature f;
196 f.setAttributes( dissolveAttrs );
197
198 if ( !keepDisjointSeparate )
199 {
200 f.setGeometry( finalGeometry );
201 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
202 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
203 }
204 else
205 {
206 for ( auto partIt = finalGeometry.const_parts_begin(); partIt != finalGeometry.const_parts_end(); ++partIt )
207 {
208 if ( const QgsAbstractGeometry *part = *partIt )
209 {
210 f.setGeometry( QgsGeometry( part->clone() ) );
211 if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
212 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
213 }
214 }
215 }
216 }
217
218 QVariantMap outputs;
219 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
220 return outputs;
221}
222
223Qgis::ProcessingAlgorithmFlags QgsBufferAlgorithm::flags() const
224{
227 return f;
228}
229
230QgsProcessingAlgorithm::VectorProperties QgsBufferAlgorithm::sinkProperties( const QString &sink, const QVariantMap &parameters, QgsProcessingContext &context, const QMap<QString, QgsProcessingAlgorithm::VectorProperties> &sourceProperties ) const
231{
232 const bool keepDisjointSeparate = parameterAsBoolean( parameters, QStringLiteral( "SEPARATE_DISJOINT" ), context );
233
235 if ( sink == QLatin1String( "OUTPUT" ) )
236 {
237 if ( sourceProperties.value( QStringLiteral( "INPUT" ) ).availability == Qgis::ProcessingPropertyAvailability::Available )
238 {
239 const VectorProperties inputProps = sourceProperties.value( QStringLiteral( "INPUT" ) );
240 result.fields = inputProps.fields;
241 result.crs = inputProps.crs;
242 result.wkbType = keepDisjointSeparate ? Qgis::WkbType::Polygon : Qgis::WkbType::MultiPolygon;
244 return result;
245 }
246 else
247 {
248 std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
249 if ( source )
250 {
251 result.fields = source->fields();
252 result.crs = source->sourceCrs();
253 result.wkbType = keepDisjointSeparate ? Qgis::WkbType::Polygon : Qgis::WkbType::MultiPolygon;
255 return result;
256 }
257 }
258 }
259 return result;
260}
261
262bool QgsBufferAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
263{
264 const QgsVectorLayer *vlayer = qobject_cast< const QgsVectorLayer * >( layer );
265 if ( !vlayer )
266 return false;
267 //Only Polygons
268 return vlayer->wkbType() == Qgis::WkbType::Polygon || vlayer->wkbType() == Qgis::WkbType::MultiPolygon;
269}
270
@ VectorPolygon
Vector polygon layers.
@ Available
Properties are available.
JoinStyle
Join styles for buffers.
Definition: qgis.h:1694
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition: qgis.h:2934
EndCapStyle
End cap styles for buffers.
Definition: qgis.h:1681
@ 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.
Definition: qgsattributes.h:59
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.
This class wraps a request for features to a vector layer (or directly its vector data provider).
@ 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:56
QgsAttributes attributes
Definition: qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:160
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:230
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:167
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
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.
Definition: qgsgeometry.h:162
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
Q_GADGET bool isNull
Definition: qgsgeometry.h:164
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:75
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
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.
Definition: qgsexception.h:83
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.
Definition: qgsproperty.h:228
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 data sets.
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.