QGIS API Documentation  3.6.0-Noosa (5873452)
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 QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
214 {
215  return processCollection( parameters, context, feedback, [ & ]( const QVector< QgsGeometry > &parts )->QgsGeometry
216  {
217  QgsGeometry result( QgsGeometry::unaryUnion( parts ) );
218  // Geos may fail in some cases, let's try a slower but safer approach
219  // See: https://issues.qgis.org/issues/20591 - Dissolve tool failing to produce outputs
220  if ( ! result.lastError().isEmpty() && parts.count() > 2 )
221  {
222  if ( feedback->isCanceled() )
223  return result;
224 
225  feedback->pushDebugInfo( QObject::tr( "GEOS exception: taking the slower route ..." ) );
226  result = QgsGeometry();
227  for ( const auto &p : parts )
228  {
229  result = QgsGeometry::unaryUnion( QVector< QgsGeometry >() << result << p );
230  if ( feedback->isCanceled() )
231  return result;
232  }
233  }
234  if ( ! result.lastError().isEmpty() )
235  {
236  feedback->reportError( result.lastError(), true );
237  if ( result.isEmpty() )
238  throw QgsProcessingException( QObject::tr( "The algorithm returned no output." ) );
239  }
240  return result;
241  }, 10000 );
242 }
243 
244 //
245 // QgsCollectAlgorithm
246 //
247 
248 QString QgsCollectAlgorithm::name() const
249 {
250  return QStringLiteral( "collect" );
251 }
252 
253 QString QgsCollectAlgorithm::displayName() const
254 {
255  return QObject::tr( "Collect geometries" );
256 }
257 
258 QStringList QgsCollectAlgorithm::tags() const
259 {
260  return QObject::tr( "union,combine,collect,multipart,parts,single" ).split( ',' );
261 }
262 
263 QString QgsCollectAlgorithm::group() const
264 {
265  return QObject::tr( "Vector geometry" );
266 }
267 
268 QString QgsCollectAlgorithm::groupId() const
269 {
270  return QStringLiteral( "vectorgeometry" );
271 }
272 
273 QVariantMap QgsCollectAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
274 {
275  return processCollection( parameters, context, feedback, []( const QVector< QgsGeometry > &parts )->QgsGeometry
276  {
277  return QgsGeometry::collectGeometry( parts );
278  } );
279 }
280 
281 
282 void QgsCollectAlgorithm::initAlgorithm( const QVariantMap & )
283 {
284  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
285  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Unique ID fields" ), QVariant(),
286  QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, true, true ) );
287 
288  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Collected" ) ) );
289 }
290 
291 QString QgsCollectAlgorithm::shortHelpString() const
292 {
293  return QObject::tr( "This algorithm takes a vector layer and collects its geometries into new multipart geometries. One or more attributes can "
294  "be specified to collect only geometries belonging to the same class (having the same value for the specified attributes), alternatively "
295  "all geometries can be collected." ) +
296  QStringLiteral( "\n\n" ) +
297  QObject::tr( "All output geometries will be converted to multi geometries, even those with just a single part. "
298  "This algorithm does not dissolve overlapping geometries - they will be collected together without modifying the shape of each geometry part." ) +
299  QStringLiteral( "\n\n" ) +
300  QObject::tr( "See the 'Promote to multipart' or 'Aggregate' algorithms for alternative options." );
301 }
302 
303 QgsCollectAlgorithm *QgsCollectAlgorithm::createInstance() const
304 {
305  return new QgsCollectAlgorithm();
306 }
307 
308 
309 
310 
Wrapper for iterator of features from vector data provider or vector layer.
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 isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
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
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
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.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
An input feature source (such as vector layers) parameter for processing algorithms.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
QgsGeometry geometry
Definition: qgsfeature.h:67
bool nextFeature(QgsFeature &f)
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
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