QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsalgorithmmultiunion.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmmultiunion.cpp
3  ------------------
4  begin : December 2021
5  copyright : (C) 2021 by Alexander Bruy
6  email : alexander dot bruy 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 "qgsalgorithmmultiunion.h"
19 
20 #include "qgsoverlayutils.h"
21 #include "qgsvectorlayer.h"
22 
24 
25 
26 QString QgsMultiUnionAlgorithm::name() const
27 {
28  return QStringLiteral( "multiunion" );
29 }
30 
31 QString QgsMultiUnionAlgorithm::displayName() const
32 {
33  return QObject::tr( "Union (multiple)" );
34 }
35 
36 QStringList QgsMultiUnionAlgorithm::tags() const
37 {
38  return QObject::tr( "union,overlap,not overlap" ).split( ',' );
39 }
40 
41 QString QgsMultiUnionAlgorithm::group() const
42 {
43  return QObject::tr( "Vector overlay" );
44 }
45 
46 QString QgsMultiUnionAlgorithm::groupId() const
47 {
48  return QStringLiteral( "vectoroverlay" );
49 }
50 
51 QString QgsMultiUnionAlgorithm::shortHelpString() const
52 {
53  return QObject::tr( "This algorithm checks overlaps between features within the Input layer and creates separate features for overlapping "
54  "and non-overlapping parts. The area of overlap will create as many identical overlapping features as there are "
55  "features that participate in that overlap." )
56  + QStringLiteral( "\n\n" )
57  + QObject::tr( "Multiple Overlay layers can also be used, in which case features from each layer are split at their overlap with features from "
58  "all other layers, creating a layer containing all the portions from both Input and Overlay layers. "
59  "The attribute table of the Union layer is filled with attribute values from the respective original layer "
60  "for non-overlapping features, and attribute values from both layers for overlapping features." );
61 }
62 
63 QgsProcessingAlgorithm *QgsMultiUnionAlgorithm::createInstance() const
64 {
65  return new QgsMultiUnionAlgorithm();
66 }
67 
68 void QgsMultiUnionAlgorithm::initAlgorithm( const QVariantMap & )
69 {
70  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
71  addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "OVERLAYS" ), QObject::tr( "Overlay layers" ), QgsProcessing::TypeVectorAnyGeometry, QVariant(), true ) );
72 
73  std::unique_ptr< QgsProcessingParameterString > prefix = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "OVERLAY_FIELDS_PREFIX" ), QObject::tr( "Overlay fields prefix" ), QString(), false, true );
74  prefix->setFlags( prefix->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
75  addParameter( prefix.release() );
76 
77  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Union" ) ) );
78 }
79 
80 QVariantMap QgsMultiUnionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
81 {
82  std::unique_ptr< QgsFeatureSource > sourceA( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
83  if ( !sourceA )
84  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
85 
86  const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "OVERLAYS" ), context );
87 
88  // loop through overlay layers and check whether they are vectors
89  long totalLayerCount = 0;
90  for ( QgsMapLayer *layer : layers )
91  {
92  if ( feedback->isCanceled() )
93  break;
94 
95  if ( !layer )
96  throw QgsProcessingException( QObject::tr( "Error retrieving map layer." ) );
97 
98  if ( layer->type() != QgsMapLayerType::VectorLayer )
99  throw QgsProcessingException( QObject::tr( "All layers must be vector layers!" ) );
100 
101  totalLayerCount++;
102  }
103 
104  const QgsWkbTypes::Type geometryType = QgsWkbTypes::multiType( sourceA->wkbType() );
105  const QgsCoordinateReferenceSystem crs = sourceA->sourceCrs();
106  const QString overlayFieldsPrefix = parameterAsString( parameters, QStringLiteral( "OVERLAY_FIELDS_PREFIX" ), context );
107  std::unique_ptr< QgsFeatureSink > sink;
108  QVariantMap outputs;
109  bool ok;
110 
111  if ( totalLayerCount == 0 )
112  {
113  // we are doing single layer union
114  QString dest;
115  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, sourceA->fields(), geometryType, sourceA->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
116  if ( !sink )
117  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
118 
119  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
120 
121  QgsOverlayUtils::resolveOverlaps( *sourceA, *sink, feedback );
122  return outputs;
123  }
124  else
125  {
126  QgsProcessingMultiStepFeedback multiStepFeedback( totalLayerCount, feedback );
127  QgsVectorLayer *unionLayer = nullptr;
128  QgsFields fields;
129 
130  long i = 0;
131  for ( QgsMapLayer *layer : layers )
132  {
133  if ( feedback->isCanceled() )
134  break;
135 
136  multiStepFeedback.setCurrentStep( i );
137 
138  if ( !layer )
139  continue;
140 
141  QgsVectorLayer *overlayLayer = qobject_cast< QgsVectorLayer * >( layer );
142  if ( !overlayLayer )
143  continue;
144 
145  if ( i == 0 )
146  {
147  QString id = QStringLiteral( "memory:" );
148  fields = QgsProcessingUtils::combineFields( sourceA->fields(), overlayLayer->fields(), overlayFieldsPrefix );
149  sink.reset( QgsProcessingUtils::createFeatureSink( id, context, fields, geometryType, crs, QVariantMap(), QStringList(), QStringList(), QgsFeatureSink::RegeneratePrimaryKey ) );
150  ok = makeUnion( *sourceA, *overlayLayer, *sink, context, &multiStepFeedback );
151 
152  if ( !ok )
153  throw QgsProcessingException( QObject::tr( "Interrupted by user." ) );
154 
155  unionLayer = qobject_cast< QgsVectorLayer * >( QgsProcessingUtils::mapLayerFromString( id, context ) );
156  }
157  else if ( i == totalLayerCount - 1 )
158  {
159  fields = QgsProcessingUtils::combineFields( unionLayer->fields(), overlayLayer->fields(), overlayFieldsPrefix );
160 
161 
162  QString dest;
163  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, geometryType, crs, QgsFeatureSink::RegeneratePrimaryKey ) );
164  if ( !sink )
165  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
166 
167  outputs.insert( QStringLiteral( "OUTPUT" ), dest );
168  ok = makeUnion( *unionLayer, *overlayLayer, *sink, context, &multiStepFeedback );
169  if ( !ok )
170  throw QgsProcessingException( QObject::tr( "Interrupted by user." ) );
171  }
172  else
173  {
174  QString id = QStringLiteral( "memory:" );
175  fields = QgsProcessingUtils::combineFields( unionLayer->fields(), overlayLayer->fields(), overlayFieldsPrefix );
176  sink.reset( QgsProcessingUtils::createFeatureSink( id, context, fields, geometryType, crs, QVariantMap(), QStringList(), QStringList(), QgsFeatureSink::RegeneratePrimaryKey ) );
177  ok = makeUnion( *unionLayer, *overlayLayer, *sink, context, &multiStepFeedback );
178  if ( !ok )
179  throw QgsProcessingException( QObject::tr( "Interrupted by user." ) );
180 
181  unionLayer = qobject_cast< QgsVectorLayer * >( QgsProcessingUtils::mapLayerFromString( id, context ) );
182  }
183 
184  i++;
185  }
186  }
187 
188  return outputs;
189 }
190 
191 bool QgsMultiUnionAlgorithm::makeUnion( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
192 {
193  const QList<int> fieldIndicesA = QgsProcessingUtils::fieldNamesToIndices( QStringList(), sourceA.fields() );
194  const QList<int> fieldIndicesB = QgsProcessingUtils::fieldNamesToIndices( QStringList(), sourceB.fields() );
195 
196  long count = 0;
197  const long total = sourceA.featureCount() * 2 + sourceB.featureCount();
198 
199  QgsOverlayUtils::intersection( sourceA, sourceB, sink, context, feedback, count, total, fieldIndicesA, fieldIndicesB );
200  if ( feedback->isCanceled() )
201  return false;
202 
203  QgsOverlayUtils::difference( sourceA, sourceB, sink, context, feedback, count, total, QgsOverlayUtils::OutputAB );
204  if ( feedback->isCanceled() )
205  return false;
206 
207  QgsOverlayUtils::difference( sourceB, sourceA, sink, context, feedback, count, total, QgsOverlayUtils::OutputBA );
208  return true;
209 }
210 
QgsProcessingUtils::createFeatureSink
static QgsFeatureSink * createFeatureSink(QString &destination, QgsProcessingContext &context, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, const QVariantMap &createOptions=QVariantMap(), const QStringList &datasourceOptions=QStringList(), const QStringList &layerOptions=QStringList(), QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QgsRemappingSinkDefinition *remappingDefinition=nullptr)
Creates a feature sink ready for adding features.
Definition: qgsprocessingutils.cpp:792
QgsMapLayerType::VectorLayer
@ VectorLayer
Vector layer.
QgsProcessingFeedback
Base class for providing feedback from a processing algorithm.
Definition: qgsprocessingfeedback.h:37
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsFeedback::isCanceled
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:67
QgsFeatureSource
An interface for objects which provide features via a getFeatures method.
Definition: qgsfeaturesource.h:37
QgsProcessingParameterDefinition::FlagAdvanced
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition: qgsprocessingparameters.h:451
QgsProcessingParameterFeatureSource
An input feature source (such as vector layers) parameter for processing algorithms.
Definition: qgsprocessingparameters.h:3057
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
QgsWkbTypes::multiType
static Type multiType(Type type) SIP_HOLDGIL
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:304
QgsProcessingParameterMultipleLayers
A parameter for processing algorithms which accepts multiple map layers.
Definition: qgsprocessingparameters.h:2097
QgsProcessingUtils::combineFields
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
Definition: qgsprocessingutils.cpp:1238
QgsProcessingParameterFeatureSink
A feature sink output for processing algorithms.
Definition: qgsprocessingparameters.h:3219
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
QgsProcessingMultiStepFeedback
Processing feedback object for multi-step operations.
Definition: qgsprocessingfeedback.h:166
QgsProcessingContext
Contains information about the context in which a processing algorithm is executed.
Definition: qgsprocessingcontext.h:46
QgsProcessingUtils::fieldNamesToIndices
static QList< int > fieldNamesToIndices(const QStringList &fieldNames, const QgsFields &fields)
Returns a list of field indices parsed from the given list of field names.
Definition: qgsprocessingutils.cpp:1274
QgsProcessing::TypeVectorAnyGeometry
@ TypeVectorAnyGeometry
Any vector layer with geometry.
Definition: qgsprocessing.h:48
QgsFeatureSource::featureCount
virtual long long featureCount() const =0
Returns the number of features contained in the source, or -1 if the feature count is unknown.
QgsFeatureSink::RegeneratePrimaryKey
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
Definition: qgsfeaturesink.h:55
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsFeatureSource::fields
virtual QgsFields fields() const =0
Returns the fields associated with features in the source.
qgsvectorlayer.h
QgsProcessingAlgorithm
Abstract base class for processing algorithms.
Definition: qgsprocessingalgorithm.h:52
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsProcessingUtils::mapLayerFromString
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType)
Interprets a string as a map layer within the supplied context.
Definition: qgsprocessingutils.cpp:376
qgsoverlayutils.h
QgsProcessingException
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsFeatureSink
An interface for objects which accept features via addFeature(s) methods.
Definition: qgsfeaturesink.h:33
qgsalgorithmmultiunion.h