Branch data Line data Source code
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 "qgswkbtypes.h"
20 : : #include "qgsvectorlayer.h"
21 : :
22 : : ///@cond PRIVATE
23 : :
24 : 0 : QString QgsBufferAlgorithm::name() const
25 : : {
26 : 0 : return QStringLiteral( "buffer" );
27 : : }
28 : :
29 : 0 : QString QgsBufferAlgorithm::displayName() const
30 : : {
31 : 0 : return QObject::tr( "Buffer" );
32 : : }
33 : :
34 : 0 : QStringList QgsBufferAlgorithm::tags() const
35 : : {
36 : 0 : return QObject::tr( "buffer,grow,fixed,variable,distance" ).split( ',' );
37 : 0 : }
38 : :
39 : 0 : QString QgsBufferAlgorithm::group() const
40 : : {
41 : 0 : return QObject::tr( "Vector geometry" );
42 : : }
43 : :
44 : 0 : QString QgsBufferAlgorithm::groupId() const
45 : : {
46 : 0 : return QStringLiteral( "vectorgeometry" );
47 : : }
48 : :
49 : 0 : void QgsBufferAlgorithm::initAlgorithm( const QVariantMap & )
50 : : {
51 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
52 : :
53 : 0 : auto bufferParam = std::make_unique < QgsProcessingParameterDistance >( QStringLiteral( "DISTANCE" ), QObject::tr( "Distance" ), 10, QStringLiteral( "INPUT" ) );
54 : 0 : bufferParam->setIsDynamic( true );
55 : 0 : bufferParam->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "Distance" ), QObject::tr( "Buffer distance" ), QgsPropertyDefinition::Double ) );
56 : 0 : bufferParam->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
57 : 0 : addParameter( bufferParam.release() );
58 : 0 : auto segmentParam = std::make_unique < QgsProcessingParameterNumber >( QStringLiteral( "SEGMENTS" ), QObject::tr( "Segments" ), QgsProcessingParameterNumber::Integer, 5, false, 1 );
59 : 0 : 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 : 0 : addParameter( segmentParam.release() );
61 : 0 : 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 : 0 : addParameter( new QgsProcessingParameterEnum( QStringLiteral( "JOIN_STYLE" ), QObject::tr( "Join style" ), QStringList() << QObject::tr( "Round" ) << QObject::tr( "Miter" ) << QObject::tr( "Bevel" ), false, 0 ) );
63 : 0 : addParameter( new QgsProcessingParameterNumber( QStringLiteral( "MITER_LIMIT" ), QObject::tr( "Miter limit" ), QgsProcessingParameterNumber::Double, 2, false, 1 ) );
64 : :
65 : 0 : addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "DISSOLVE" ), QObject::tr( "Dissolve result" ), false ) );
66 : 0 : addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Buffered" ), QgsProcessing::TypeVectorPolygon, QVariant(), false, true, true ) );
67 : 0 : }
68 : :
69 : 0 : QString QgsBufferAlgorithm::shortHelpString() const
70 : : {
71 : 0 : 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"
72 : : "The segments parameter controls the number of line segments to use to approximate a quarter circle when creating rounded offsets.\n\n"
73 : : "The end cap style parameter controls how line endings are handled in the buffer.\n\n"
74 : : "The join style parameter specifies whether round, miter or beveled joins should be used when offsetting corners in a line.\n\n"
75 : : "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." );
76 : : }
77 : :
78 : 0 : QgsBufferAlgorithm *QgsBufferAlgorithm::createInstance() const
79 : : {
80 : 0 : return new QgsBufferAlgorithm();
81 : : }
82 : :
83 : 0 : QVariantMap QgsBufferAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
84 : : {
85 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
86 : 0 : if ( !source )
87 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
88 : :
89 : 0 : QString dest;
90 : 0 : std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), QgsWkbTypes::MultiPolygon, source->sourceCrs() ) );
91 : 0 : if ( !sink )
92 : 0 : throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
93 : :
94 : : // fixed parameters
95 : 0 : bool dissolve = parameterAsBoolean( parameters, QStringLiteral( "DISSOLVE" ), context );
96 : 0 : int segments = parameterAsInt( parameters, QStringLiteral( "SEGMENTS" ), context );
97 : 0 : QgsGeometry::EndCapStyle endCapStyle = static_cast< QgsGeometry::EndCapStyle >( 1 + parameterAsInt( parameters, QStringLiteral( "END_CAP_STYLE" ), context ) );
98 : 0 : QgsGeometry::JoinStyle joinStyle = static_cast< QgsGeometry::JoinStyle>( 1 + parameterAsInt( parameters, QStringLiteral( "JOIN_STYLE" ), context ) );
99 : 0 : double miterLimit = parameterAsDouble( parameters, QStringLiteral( "MITER_LIMIT" ), context );
100 : 0 : double bufferDistance = parameterAsDouble( parameters, QStringLiteral( "DISTANCE" ), context );
101 : 0 : bool dynamicBuffer = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
102 : 0 : QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() );
103 : 0 : QgsProperty bufferProperty;
104 : 0 : if ( dynamicBuffer )
105 : : {
106 : 0 : bufferProperty = parameters.value( QStringLiteral( "DISTANCE" ) ).value< QgsProperty >();
107 : 0 : }
108 : :
109 : 0 : long count = source->featureCount();
110 : :
111 : 0 : QgsFeature f;
112 : : // buffer doesn't care about invalid features, and buffering can be used to repair geometries
113 : 0 : QgsFeatureIterator it = source->getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
114 : :
115 : 0 : double step = count > 0 ? 100.0 / count : 1;
116 : 0 : int current = 0;
117 : :
118 : 0 : QVector< QgsGeometry > bufferedGeometriesForDissolve;
119 : 0 : QgsAttributes dissolveAttrs;
120 : :
121 : 0 : while ( it.nextFeature( f ) )
122 : : {
123 : 0 : if ( feedback->isCanceled() )
124 : : {
125 : 0 : break;
126 : : }
127 : 0 : if ( dissolveAttrs.isEmpty() )
128 : 0 : dissolveAttrs = f.attributes();
129 : :
130 : 0 : QgsFeature out = f;
131 : 0 : if ( out.hasGeometry() )
132 : : {
133 : 0 : double distance = bufferDistance;
134 : 0 : if ( dynamicBuffer )
135 : : {
136 : 0 : expressionContext.setFeature( f );
137 : 0 : distance = bufferProperty.valueAsDouble( expressionContext, bufferDistance );
138 : 0 : }
139 : :
140 : 0 : QgsGeometry outputGeometry = f.geometry().buffer( distance, segments, endCapStyle, joinStyle, miterLimit );
141 : 0 : if ( outputGeometry.isNull() )
142 : : {
143 : 0 : QgsMessageLog::logMessage( QObject::tr( "Error calculating buffer for feature %1" ).arg( f.id() ), QObject::tr( "Processing" ), Qgis::Warning );
144 : 0 : }
145 : 0 : if ( dissolve )
146 : 0 : bufferedGeometriesForDissolve << outputGeometry;
147 : : else
148 : : {
149 : 0 : outputGeometry.convertToMultiType();
150 : 0 : out.setGeometry( outputGeometry );
151 : : }
152 : 0 : }
153 : :
154 : 0 : if ( !dissolve )
155 : 0 : sink->addFeature( out, QgsFeatureSink::FastInsert );
156 : :
157 : 0 : feedback->setProgress( current * step );
158 : 0 : current++;
159 : 0 : }
160 : :
161 : 0 : if ( dissolve )
162 : : {
163 : 0 : QgsGeometry finalGeometry = QgsGeometry::unaryUnion( bufferedGeometriesForDissolve );
164 : 0 : finalGeometry.convertToMultiType();
165 : 0 : QgsFeature f;
166 : 0 : f.setGeometry( finalGeometry );
167 : 0 : f.setAttributes( dissolveAttrs );
168 : 0 : sink->addFeature( f, QgsFeatureSink::FastInsert );
169 : 0 : }
170 : :
171 : 0 : QVariantMap outputs;
172 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), dest );
173 : 0 : return outputs;
174 : 0 : }
175 : :
176 : 0 : QgsProcessingAlgorithm::Flags QgsBufferAlgorithm::flags() const
177 : : {
178 : 0 : Flags f = QgsProcessingAlgorithm::flags();
179 : 0 : f |= QgsProcessingAlgorithm::FlagSupportsInPlaceEdits;
180 : 0 : return f;
181 : : }
182 : :
183 : 0 : QgsProcessingAlgorithm::VectorProperties QgsBufferAlgorithm::sinkProperties( const QString &sink, const QVariantMap ¶meters, QgsProcessingContext &context, const QMap<QString, QgsProcessingAlgorithm::VectorProperties> &sourceProperties ) const
184 : : {
185 : 0 : QgsProcessingAlgorithm::VectorProperties result;
186 : 0 : if ( sink == QLatin1String( "OUTPUT" ) )
187 : : {
188 : 0 : if ( sourceProperties.value( QStringLiteral( "INPUT" ) ).availability == QgsProcessingAlgorithm::Available )
189 : : {
190 : 0 : const VectorProperties inputProps = sourceProperties.value( QStringLiteral( "INPUT" ) );
191 : 0 : result.fields = inputProps.fields;
192 : 0 : result.crs = inputProps.crs;
193 : 0 : result.wkbType = QgsWkbTypes::MultiPolygon;
194 : 0 : result.availability = Available;
195 : 0 : return result;
196 : 0 : }
197 : : else
198 : : {
199 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
200 : 0 : if ( source )
201 : : {
202 : 0 : result.fields = source->fields();
203 : 0 : result.crs = source->sourceCrs();
204 : 0 : result.wkbType = QgsWkbTypes::MultiPolygon;
205 : 0 : result.availability = Available;
206 : 0 : return result;
207 : : }
208 : 0 : }
209 : 0 : }
210 : 0 : return result;
211 : 0 : }
212 : :
213 : 0 : bool QgsBufferAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
214 : : {
215 : 0 : const QgsVectorLayer *vlayer = qobject_cast< const QgsVectorLayer * >( layer );
216 : 0 : if ( !vlayer )
217 : 0 : return false;
218 : : //Only Polygons
219 : 0 : return vlayer->wkbType() == QgsWkbTypes::Type::Polygon || vlayer->wkbType() == QgsWkbTypes::Type::MultiPolygon;
220 : 0 : }
221 : :
222 : : ///@endcond
|