QGIS API Documentation  3.0.2-Girona (307d082)
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( QObject::tr( "Could not load source layer for 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( QObject::tr( "Could not create destination layer for 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() )
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() )
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( "Unique ID fields" ), 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 polygon or line vector layer and combines their geometries into new geometries. One or more attributes can "
202  "be specified to dissolve only geometries belonging to the same class (having the same value for the specified attributes), alternatively "
203  "all geometries can be dissolved.\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  return QgsGeometry::unaryUnion( parts );
218  }, 10000 );
219 }
220 
221 //
222 // QgsCollectAlgorithm
223 //
224 
225 QString QgsCollectAlgorithm::name() const
226 {
227  return QStringLiteral( "collect" );
228 }
229 
230 QString QgsCollectAlgorithm::displayName() const
231 {
232  return QObject::tr( "Collect geometries" );
233 }
234 
235 QStringList QgsCollectAlgorithm::tags() const
236 {
237  return QObject::tr( "union,combine,collect,multipart,parts,single" ).split( ',' );
238 }
239 
240 QString QgsCollectAlgorithm::group() const
241 {
242  return QObject::tr( "Vector geometry" );
243 }
244 
245 QString QgsCollectAlgorithm::groupId() const
246 {
247  return QStringLiteral( "vectorgeometry" );
248 }
249 
250 QVariantMap QgsCollectAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
251 {
252  return processCollection( parameters, context, feedback, []( const QVector< QgsGeometry > &parts )->QgsGeometry
253  {
254  return QgsGeometry::collectGeometry( parts );
255  } );
256 }
257 
258 
259 void QgsCollectAlgorithm::initAlgorithm( const QVariantMap & )
260 {
261  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
262  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Unique ID fields" ), QVariant(),
263  QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, true, true ) );
264 
265  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Collected" ) ) );
266 }
267 
268 QString QgsCollectAlgorithm::shortHelpString() const
269 {
270  return QObject::tr( "This algorithm takes a vector layer and collects its geometries into new multipart geometries. One or more attributes can "
271  "be specified to collect only geometries belonging to the same class (having the same value for the specified attributes), alternatively "
272  "all geometries can be collected." ) +
273  QStringLiteral( "\n\n" ) +
274  QObject::tr( "All output geometries will be converted to multi geometries, even those with just a single part. "
275  "This algorithm does not dissolve overlapping geometries - they will be collected together without modifying the shape of each geometry part." ) +
276  QStringLiteral( "\n\n" ) +
277  QObject::tr( "See the 'Promote to multipart' or 'Aggregate' algorithms for alternative options." );
278 }
279 
280 QgsCollectAlgorithm *QgsCollectAlgorithm::createInstance() const
281 {
282  return new QgsCollectAlgorithm();
283 }
284 
285 
286 
287 
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:296
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:111
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:62
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
A feature sink output for processing algorithms.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
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
bool nextFeature(QgsFeature &f)
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
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.
QgsAttributes attributes
Definition: qgsfeature.h:72