QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsalgorithmdissolve.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmdissolve.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 "qgsalgorithmdissolve.h"
19 
21 
22 //
23 // QgsCollectorAlgorithm
24 //
25 
26 QVariantMap QgsCollectorAlgorithm::processCollection( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback,
27  const std::function<QgsGeometry( const QVector< QgsGeometry >& )> &collector, int maxQueueLength )
28 {
29  std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
30  if ( !source )
31  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
32 
33  QString dest;
34  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), QgsWkbTypes::multiType( source->wkbType() ), source->sourceCrs() ) );
35 
36  if ( !sink )
37  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
38 
39  QStringList fields = parameterAsFields( parameters, QStringLiteral( "FIELD" ), context );
40 
41  long count = source->featureCount();
42 
43  QgsFeature f;
44  QgsFeatureIterator it = source->getFeatures();
45 
46  double step = count > 0 ? 100.0 / count : 1;
47  int current = 0;
48 
49  if ( fields.isEmpty() )
50  {
51  // dissolve all - not using fields
52  bool firstFeature = true;
53  // we dissolve geometries in blocks using unaryUnion
54  QVector< QgsGeometry > geomQueue;
55  QgsFeature outputFeature;
56 
57  while ( it.nextFeature( f ) )
58  {
59  if ( feedback->isCanceled() )
60  {
61  break;
62  }
63 
64  if ( firstFeature )
65  {
66  outputFeature = f;
67  firstFeature = false;
68  }
69 
70  if ( f.hasGeometry() && !f.geometry().isNull() )
71  {
72  geomQueue.append( f.geometry() );
73  if ( maxQueueLength > 0 && geomQueue.length() > maxQueueLength )
74  {
75  // queue too long, combine it
76  QgsGeometry tempOutputGeometry = collector( geomQueue );
77  geomQueue.clear();
78  geomQueue << tempOutputGeometry;
79  }
80  }
81 
82  feedback->setProgress( current * step );
83  current++;
84  }
85 
86  outputFeature.setGeometry( collector( geomQueue ) );
87  sink->addFeature( outputFeature, QgsFeatureSink::FastInsert );
88  }
89  else
90  {
91  QList< int > fieldIndexes;
92  Q_FOREACH ( const QString &field, fields )
93  {
94  int index = source->fields().lookupField( field );
95  if ( index >= 0 )
96  fieldIndexes << index;
97  }
98 
99  QHash< QVariant, QgsAttributes > attributeHash;
100  QHash< QVariant, QVector< QgsGeometry > > geometryHash;
101 
102  while ( it.nextFeature( f ) )
103  {
104  if ( feedback->isCanceled() )
105  {
106  break;
107  }
108 
109  QVariantList indexAttributes;
110  Q_FOREACH ( int index, fieldIndexes )
111  {
112  indexAttributes << f.attribute( index );
113  }
114 
115  if ( !attributeHash.contains( indexAttributes ) )
116  {
117  // keep attributes of first feature
118  attributeHash.insert( indexAttributes, f.attributes() );
119  }
120 
121  if ( f.hasGeometry() && !f.geometry().isNull() )
122  {
123  geometryHash[ indexAttributes ].append( f.geometry() );
124  }
125  }
126 
127  int numberFeatures = attributeHash.count();
128  QHash< QVariant, QgsAttributes >::const_iterator attrIt = attributeHash.constBegin();
129  for ( ; attrIt != attributeHash.constEnd(); ++attrIt )
130  {
131  if ( feedback->isCanceled() )
132  {
133  break;
134  }
135 
136  QgsFeature outputFeature;
137  if ( geometryHash.contains( attrIt.key() ) )
138  {
139  QgsGeometry geom = collector( geometryHash.value( attrIt.key() ) );
140  if ( !geom.isMultipart() )
141  {
142  geom.convertToMultiType();
143  }
144  outputFeature.setGeometry( geom );
145  }
146  outputFeature.setAttributes( attrIt.value() );
147  sink->addFeature( outputFeature, QgsFeatureSink::FastInsert );
148 
149  feedback->setProgress( current * 100.0 / numberFeatures );
150  current++;
151  }
152  }
153 
154  QVariantMap outputs;
155  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
156  return outputs;
157 }
158 
159 
160 //
161 // QgsDissolveAlgorithm
162 //
163 
164 QString QgsDissolveAlgorithm::name() const
165 {
166  return QStringLiteral( "dissolve" );
167 }
168 
169 QString QgsDissolveAlgorithm::displayName() const
170 {
171  return QObject::tr( "Dissolve" );
172 }
173 
174 QStringList QgsDissolveAlgorithm::tags() const
175 {
176  return QObject::tr( "dissolve,union,combine,collect" ).split( ',' );
177 }
178 
179 QString QgsDissolveAlgorithm::group() const
180 {
181  return QObject::tr( "Vector geometry" );
182 }
183 
184 QString QgsDissolveAlgorithm::groupId() const
185 {
186  return QStringLiteral( "vectorgeometry" );
187 }
188 
189 
190 void QgsDissolveAlgorithm::initAlgorithm( const QVariantMap & )
191 {
192  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
193  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Dissolve field(s)" ), QVariant(),
194  QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, true, true ) );
195 
196  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Dissolved" ) ) );
197 }
198 
199 QString QgsDissolveAlgorithm::shortHelpString() const
200 {
201  return QObject::tr( "This algorithm takes a vector layer and combines their features into new features. One or more attributes can "
202  "be specified to dissolve features belonging to the same class (having the same value for the specified attributes), alternatively "
203  "all features can be dissolved in a single one.\n\n"
204  "All output geometries will be converted to multi geometries. "
205  "In case the input is a polygon layer, common boundaries of adjacent polygons being dissolved will get erased." );
206 }
207 
208 QgsDissolveAlgorithm *QgsDissolveAlgorithm::createInstance() const
209 {
210  return new QgsDissolveAlgorithm();
211 }
212 
213 
214 QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
215 {
216  return processCollection( parameters, context, feedback, [ & ]( const QVector< QgsGeometry > &parts )->QgsGeometry
217  {
218  QgsGeometry result( QgsGeometry::unaryUnion( parts ) );
219  // Geos may fail in some cases, let's try a slower but safer approach
220  // See: https://issues.qgis.org/issues/20591 - Dissolve tool failing to produce outputs
221  if ( ! result.lastError().isEmpty() && parts.count() > 2 )
222  {
223  if ( feedback->isCanceled() )
224  return result;
225 
226  feedback->pushDebugInfo( QStringLiteral( "GEOS exception: taking the slower route ..." ) );
227  result = QgsGeometry();
228  for ( const auto &p : parts )
229  {
230  result = QgsGeometry::unaryUnion( QVector< QgsGeometry >() << result << p );
231  if ( feedback->isCanceled() )
232  return result;
233  }
234  }
235  if ( ! result.lastError().isEmpty() )
236  {
237  feedback->reportError( result.lastError(), true );
238  if ( result.isEmpty() )
239  throw QgsProcessingException( QObject::tr( "The algorithm returned no output." ) );
240  }
241  return result;
242  }, 10000 );
243 }
244 
245 //
246 // QgsCollectAlgorithm
247 //
248 
249 QString QgsCollectAlgorithm::name() const
250 {
251  return QStringLiteral( "collect" );
252 }
253 
254 QString QgsCollectAlgorithm::displayName() const
255 {
256  return QObject::tr( "Collect geometries" );
257 }
258 
259 QStringList QgsCollectAlgorithm::tags() const
260 {
261  return QObject::tr( "union,combine,collect,multipart,parts,single" ).split( ',' );
262 }
263 
264 QString QgsCollectAlgorithm::group() const
265 {
266  return QObject::tr( "Vector geometry" );
267 }
268 
269 QString QgsCollectAlgorithm::groupId() const
270 {
271  return QStringLiteral( "vectorgeometry" );
272 }
273 
274 QVariantMap QgsCollectAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
275 {
276  return processCollection( parameters, context, feedback, []( const QVector< QgsGeometry > &parts )->QgsGeometry
277  {
278  return QgsGeometry::collectGeometry( parts );
279  } );
280 }
281 
282 
283 void QgsCollectAlgorithm::initAlgorithm( const QVariantMap & )
284 {
285  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
286  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Unique ID fields" ), QVariant(),
287  QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, true, true ) );
288 
289  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Collected" ) ) );
290 }
291 
292 QString QgsCollectAlgorithm::shortHelpString() const
293 {
294  return QObject::tr( "This algorithm takes a vector layer and collects its geometries into new multipart geometries. One or more attributes can "
295  "be specified to collect only geometries belonging to the same class (having the same value for the specified attributes), alternatively "
296  "all geometries can be collected." ) +
297  QStringLiteral( "\n\n" ) +
298  QObject::tr( "All output geometries will be converted to multi geometries, even those with just a single part. "
299  "This algorithm does not dissolve overlapping geometries - they will be collected together without modifying the shape of each geometry part." ) +
300  QStringLiteral( "\n\n" ) +
301  QObject::tr( "See the 'Promote to multipart' or 'Aggregate' algorithms for alternative options." );
302 }
303 
304 QgsCollectAlgorithm *QgsCollectAlgorithm::createInstance() const
305 {
306  return new QgsCollectAlgorithm();
307 }
308 
309 
310 
311 
Wrapper for iterator of features from vector data provider or vector layer.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
static Type multiType(Type type)
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:298
Base class for providing feedback from a processing algorithm.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
A vector layer or feature source field parameter for processing algorithms.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
A feature sink output for processing algorithms.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
virtual void pushDebugInfo(const QString &info)
Pushes an informational message containing debugging helpers from the algorithm.
bool convertToMultiType()
Converts single type geometry into multitype geometry e.g.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
An input feature source (such as vector layers) parameter for processing algorithms.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
QgsGeometry geometry
Definition: qgsfeature.h:67
bool nextFeature(QgsFeature &f)
Contains information about the context in which a processing algorithm is executed.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
QgsAttributes attributes
Definition: qgsfeature.h:65