QGIS API Documentation 4.3.0-Master (d3b565c628d)
Loading...
Searching...
No Matches
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
19
20#include "qgsoverlayutils.h"
21#include "qgsvectorlayer.h"
22
23#include <QString>
24
25using namespace Qt::StringLiterals;
26
28
29
30QString QgsMultiUnionAlgorithm::name() const
31{
32 return u"multiunion"_s;
33}
34
35QString QgsMultiUnionAlgorithm::displayName() const
36{
37 return QObject::tr( "Union (multiple)" );
38}
39
40QStringList QgsMultiUnionAlgorithm::tags() const
41{
42 return QObject::tr( "union,overlap,not overlap" ).split( ',' );
43}
44
45QString QgsMultiUnionAlgorithm::group() const
46{
47 return QObject::tr( "Vector overlay" );
48}
49
50QString QgsMultiUnionAlgorithm::groupId() const
51{
52 return u"vectoroverlay"_s;
53}
54
55QString QgsMultiUnionAlgorithm::shortHelpString() const
56{
57 return QObject::tr(
58 "This algorithm checks overlaps between features within the Input layer and creates separate features for overlapping "
59 "and non-overlapping parts. The area of overlap will create as many identical overlapping features as there are "
60 "features that participate in that overlap."
61 )
62 + u"\n\n"_s
63 + QObject::tr(
64 "Multiple Overlay layers can also be used, in which case features from each layer are split at their overlap with features from "
65 "all other layers, creating a layer containing all the portions from both Input and Overlay layers. "
66 "The attribute table of the Union layer is filled with attribute values from the respective original layer "
67 "for non-overlapping features, and attribute values from both layers for overlapping features."
68 );
69}
70
71QString QgsMultiUnionAlgorithm::shortDescription() const
72{
73 return QObject::tr(
74 "Checks overlaps between features on the same layer or on more layers "
75 "and creates separate features for overlapping and non-overlapping parts."
76 );
77}
78
79Qgis::ProcessingAlgorithmDocumentationFlags QgsMultiUnionAlgorithm::documentationFlags() const
80{
82}
83
84QgsProcessingAlgorithm *QgsMultiUnionAlgorithm::createInstance() const
85{
86 return new QgsMultiUnionAlgorithm();
87}
88
89void QgsMultiUnionAlgorithm::initAlgorithm( const QVariantMap & )
90{
91 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Input layer" ) ) );
92 addParameter( new QgsProcessingParameterMultipleLayers( u"OVERLAYS"_s, QObject::tr( "Overlay layers" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true ) );
93
94 auto prefix = std::make_unique<QgsProcessingParameterString>( u"OVERLAY_FIELDS_PREFIX"_s, QObject::tr( "Overlay fields prefix" ), QString(), false, true );
95 prefix->setFlags( prefix->flags() | Qgis::ProcessingParameterFlag::Advanced );
96 addParameter( prefix.release() );
97
98 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Union" ) ) );
99}
100
101QVariantMap QgsMultiUnionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
102{
103 QGS_MARK_ALGORITHM_SOURCE
104
105 std::unique_ptr<QgsFeatureSource> sourceA( parameterAsSource( parameters, u"INPUT"_s, context ) );
106 if ( !sourceA )
107 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
108
109 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u"OVERLAYS"_s, context );
110
111 // loop through overlay layers and check whether they are vectors
112 long totalLayerCount = 0;
113 for ( QgsMapLayer *layer : layers )
114 {
115 if ( feedback->isCanceled() )
116 break;
117
118 if ( !layer )
119 throw QgsProcessingException( QObject::tr( "Error retrieving map layer." ) );
120
121 if ( layer->type() != Qgis::LayerType::Vector )
122 throw QgsProcessingException( QObject::tr( "All layers must be vector layers!" ) );
123
124 totalLayerCount++;
125 }
126
127 const Qgis::WkbType geometryType = QgsWkbTypes::multiType( sourceA->wkbType() );
128 const QgsCoordinateReferenceSystem crs = sourceA->sourceCrs();
129 const QString overlayFieldsPrefix = parameterAsString( parameters, u"OVERLAY_FIELDS_PREFIX"_s, context );
130 std::unique_ptr<QgsFeatureSink> sink;
131 QVariantMap outputs;
132 bool ok;
133
134 if ( totalLayerCount == 0 )
135 {
136 // we are doing single layer union
137 QString dest;
138 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, sourceA->fields(), geometryType, sourceA->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
139 if ( !sink )
140 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
141
142 outputs.insert( u"OUTPUT"_s, dest );
143
144 QgsOverlayUtils::resolveOverlaps( *sourceA, *sink, u"OUTPUT"_s, feedback );
145 sink->finalize();
146 feedback->featureSinkFinalized( u"OUTPUT"_s );
147 return outputs;
148 }
149 else
150 {
151 QgsProcessingMultiStepFeedback multiStepFeedback( totalLayerCount, feedback );
152 QgsVectorLayer *unionLayer = nullptr;
153 QgsFields fields;
154
155 long i = 0;
156 for ( QgsMapLayer *layer : layers )
157 {
158 if ( feedback->isCanceled() )
159 break;
160
161 multiStepFeedback.setCurrentStep( i );
162
163 if ( !layer )
164 continue;
165
166 QgsVectorLayer *overlayLayer = qobject_cast<QgsVectorLayer *>( layer );
167 if ( !overlayLayer )
168 continue;
169
170 if ( i == 0 )
171 {
172 QString id = u"memory:"_s;
173 fields = QgsProcessingUtils::combineFields( sourceA->fields(), overlayLayer->fields(), overlayFieldsPrefix );
174 sink.reset( QgsProcessingUtils::createFeatureSink( id, context, fields, geometryType, crs, QVariantMap(), QStringList(), QStringList(), QgsFeatureSink::RegeneratePrimaryKey ) );
175 ok = makeUnion( *sourceA, *overlayLayer, *sink, QString(), context, &multiStepFeedback );
176
177 if ( !ok )
178 throw QgsProcessingException( QObject::tr( "Interrupted by user." ) );
179
180 unionLayer = qobject_cast<QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( id, context ) );
181 }
182 else if ( i == totalLayerCount - 1 )
183 {
184 fields = QgsProcessingUtils::combineFields( unionLayer->fields(), overlayLayer->fields(), overlayFieldsPrefix );
185
186
187 QString dest;
188 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, dest, fields, geometryType, crs, QgsFeatureSink::RegeneratePrimaryKey ) );
189 if ( !sink )
190 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
191
192 outputs.insert( u"OUTPUT"_s, dest );
193 ok = makeUnion( *unionLayer, *overlayLayer, *sink, u"OUTPUT"_s, context, &multiStepFeedback );
194 sink->finalize();
195 feedback->featureSinkFinalized( u"OUTPUT"_s );
196 if ( !ok )
197 throw QgsProcessingException( QObject::tr( "Interrupted by user." ) );
198 }
199 else
200 {
201 QString id = u"memory:"_s;
202 fields = QgsProcessingUtils::combineFields( unionLayer->fields(), overlayLayer->fields(), overlayFieldsPrefix );
203 sink.reset( QgsProcessingUtils::createFeatureSink( id, context, fields, geometryType, crs, QVariantMap(), QStringList(), QStringList(), QgsFeatureSink::RegeneratePrimaryKey ) );
204 ok = makeUnion( *unionLayer, *overlayLayer, *sink, QString(), context, &multiStepFeedback );
205 if ( !ok )
206 throw QgsProcessingException( QObject::tr( "Interrupted by user." ) );
207
208 unionLayer = qobject_cast<QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( id, context ) );
209 }
210
211 i++;
212 }
213 }
214
215 return outputs;
216}
217
218bool QgsMultiUnionAlgorithm::makeUnion(
219 const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, const QString &sinkName, QgsProcessingContext &context, QgsProcessingFeedback *feedback
220)
221{
222 const QList<int> fieldIndicesA = QgsProcessingUtils::fieldNamesToIndices( QStringList(), sourceA.fields() );
223 const QList<int> fieldIndicesB = QgsProcessingUtils::fieldNamesToIndices( QStringList(), sourceB.fields() );
224
225 long count = 0;
226 const long total = sourceA.featureCount() * 2 + sourceB.featureCount();
227
228 QgsOverlayUtils::intersection( sourceA, sourceB, sink, sinkName, context, feedback, count, total, fieldIndicesA, fieldIndicesB );
229 if ( feedback->isCanceled() )
230 return false;
231
232 QgsOverlayUtils::difference( sourceA, sourceB, sink, sinkName, context, feedback, count, total, QgsOverlayUtils::OutputAB );
233 if ( feedback->isCanceled() )
234 return false;
235
236 QgsOverlayUtils::difference( sourceB, sourceA, sink, sinkName, context, feedback, count, total, QgsOverlayUtils::OutputBA );
237 return true;
238}
239
@ VectorAnyGeometry
Any vector layer with geometry.
Definition qgis.h:3749
@ RegeneratesPrimaryKey
Algorithm always drops any existing primary keys or FID values and regenerates them in outputs.
Definition qgis.h:3836
@ Vector
Vector layer.
Definition qgis.h:207
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3847
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3982
Represents a coordinate reference system (CRS).
An interface for objects which accept features via addFeature(s) methods.
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
An interface for objects which provide features via a getFeatures method.
virtual QgsFields fields() const =0
Returns the fields associated with features in the source.
virtual long long featureCount() const =0
Returns the number of features contained in the source, or -1 if the feature count is unknown.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
Container of fields for a vector layer.
Definition qgsfields.h:45
Base class for all map layer types.
Definition qgsmaplayer.h:83
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
Processing feedback object for multi-step operations.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A parameter for processing algorithms which accepts multiple map layers.
static QList< int > fieldNamesToIndices(const QStringList &fieldNames, const QgsFields &fields)
Returns a list of field indices parsed from the given list of field names.
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).
static QgsFeatureSink * createFeatureSink(QString &destination, QgsProcessingContext &context, const QgsFields &fields, Qgis::WkbType 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.
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
Represents a vector layer which manages a vector based dataset.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.