QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsalgorithmmultidifference.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmmultidifference.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
24
25
26QString QgsMultiDifferenceAlgorithm::name() const
27{
28 return QStringLiteral( "multidifference" );
29}
30
31QString QgsMultiDifferenceAlgorithm::displayName() const
32{
33 return QObject::tr( "Difference (multiple)" );
34}
35
36QStringList QgsMultiDifferenceAlgorithm::tags() const
37{
38 return QObject::tr( "difference,erase,not overlap" ).split( ',' );
39}
40
41QString QgsMultiDifferenceAlgorithm::group() const
42{
43 return QObject::tr( "Vector overlay" );
44}
45
46QString QgsMultiDifferenceAlgorithm::groupId() const
47{
48 return QStringLiteral( "vectoroverlay" );
49}
50
51QString QgsMultiDifferenceAlgorithm::shortHelpString() const
52{
53 return QObject::tr( "This algorithm extracts features from the Input layer that fall completely outside or only partially overlap the features from any of the Overlay layer(s). "
54 "For each overlay layer the difference is calculated between the result of all previous difference operations and this overlay layer. "
55 "Input layer features that partially overlap feature(s) in the Overlay layers are split along those features' boundary "
56 "and only the portions outside the Overlay layer features are retained." )
57 + QStringLiteral( "\n\n" )
58 + QObject::tr( "Attributes are not modified, although properties such as area or length of the features will "
59 "be modified by the difference operation. If such properties are stored as attributes, those attributes will have to "
60 "be manually updated." );
61}
62
63QgsProcessingAlgorithm *QgsMultiDifferenceAlgorithm::createInstance() const
64{
65 return new QgsMultiDifferenceAlgorithm();
66}
67
68void QgsMultiDifferenceAlgorithm::initAlgorithm( const QVariantMap & )
69{
70 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
71 addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "OVERLAYS" ), QObject::tr( "Overlay layers" ), Qgis::ProcessingSourceType::VectorAnyGeometry ) );
72 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Difference" ) ) );
73}
74
75
76QVariantMap QgsMultiDifferenceAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
77{
78 std::unique_ptr< QgsFeatureSource > sourceA( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
79 if ( !sourceA )
80 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
81
82 const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "OVERLAYS" ), context );
83
84 // loop through overlay layers and check whether they are vectors
85 long totalLayerCount = 0;
86 for ( QgsMapLayer *layer : layers )
87 {
88 if ( feedback->isCanceled() )
89 break;
90
91 if ( !layer )
92 throw QgsProcessingException( QObject::tr( "Error retrieving map layer." ) );
93
94 if ( layer->type() != Qgis::LayerType::Vector )
95 throw QgsProcessingException( QObject::tr( "All layers must be vector layers!" ) );
96
97 totalLayerCount++;
98 }
99
100 const Qgis::WkbType geometryType = QgsWkbTypes::multiType( sourceA->wkbType() );
101 const QgsCoordinateReferenceSystem crs = sourceA->sourceCrs();
102 std::unique_ptr< QgsFeatureSink > sink;
103 long count = 0;
104 QVariantMap outputs;
105
106 if ( totalLayerCount == 1 )
107 {
108 QString dest;
109 sink.reset( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, sourceA->fields(), geometryType, crs ) );
110 if ( !sink )
111 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
112
113 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
114
115 QgsVectorLayer *overlayLayer = qobject_cast< QgsVectorLayer * >( layers.at( 0 ) );
116
117 const long total = sourceA->featureCount();
118 QgsOverlayUtils::difference( *sourceA, *overlayLayer, *sink, context, feedback, count, total, QgsOverlayUtils::OutputA );
119 }
120 else
121 {
122 QgsProcessingMultiStepFeedback multiStepFeedback( totalLayerCount, feedback );
123 QgsVectorLayer *differenceLayer = nullptr;
124
125 long i = 0;
126 for ( QgsMapLayer *layer : layers )
127 {
128 if ( feedback->isCanceled() )
129 break;
130
131 multiStepFeedback.setCurrentStep( i );
132
133 if ( !layer )
134 continue;
135
136 QgsVectorLayer *overlayLayer = qobject_cast< QgsVectorLayer * >( layer );
137 if ( !overlayLayer )
138 continue;
139
140 count = 0;
141 if ( i == 0 )
142 {
143 QString id = QStringLiteral( "memory:" );
144 sink.reset( QgsProcessingUtils::createFeatureSink( id, context, sourceA->fields(), geometryType, crs ) );
145 QgsOverlayUtils::difference( *sourceA, *overlayLayer, *sink, context, &multiStepFeedback, count, sourceA->featureCount(), QgsOverlayUtils::OutputA );
146
147 differenceLayer = qobject_cast< QgsVectorLayer * >( QgsProcessingUtils::mapLayerFromString( id, context ) );
148 }
149 else if ( i == totalLayerCount - 1 )
150 {
151 QString dest;
152 std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, differenceLayer->fields(), geometryType, crs ) );
153 if ( !sink )
154 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
155
156 outputs.insert( QStringLiteral( "OUTPUT" ), dest );
157
158 QgsOverlayUtils::difference( *differenceLayer, *overlayLayer, *sink, context, &multiStepFeedback, count, differenceLayer->featureCount(), QgsOverlayUtils::OutputA );
159 }
160 else
161 {
162 QString id = QStringLiteral( "memory:" );
163 sink.reset( QgsProcessingUtils::createFeatureSink( id, context, differenceLayer->fields(), geometryType, crs ) );
164 QgsOverlayUtils::difference( *differenceLayer, *overlayLayer, *sink, context, &multiStepFeedback, count, differenceLayer->featureCount(), QgsOverlayUtils::OutputA );
165
166 differenceLayer = qobject_cast< QgsVectorLayer * >( QgsProcessingUtils::mapLayerFromString( id, context ) );
167 }
168
169 i++;
170 }
171 }
172
173 return outputs;
174}
175
@ VectorAnyGeometry
Any vector layer with geometry.
@ Vector
Vector layer.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
This class represents a coordinate reference system (CRS).
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
Base class for all map layer types.
Definition: qgsmaplayer.h:75
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.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
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 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 data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
static Qgis::WkbType multiType(Qgis::WkbType type)
Returns the multi type for a WKB type.
Definition: qgswkbtypes.h:200
const QgsCoordinateReferenceSystem & crs