QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsalgorithmcalculateoverlaps.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmcalculateoverlaps.cpp
3 ------------------
4 begin : May 2019
5 copyright : (C) 2019 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
19
20#include "qgsdistancearea.h"
21#include "qgsgeometryengine.h"
22#include "qgsspatialindex.h"
23#include "qgsvectorlayer.h"
24
26
27QString QgsCalculateVectorOverlapsAlgorithm::name() const
28{
29 return QStringLiteral( "calculatevectoroverlaps" );
30}
31
32QString QgsCalculateVectorOverlapsAlgorithm::displayName() const
33{
34 return QObject::tr( "Overlap analysis" );
35}
36
37QStringList QgsCalculateVectorOverlapsAlgorithm::tags() const
38{
39 return QObject::tr( "vector,overlay,area,percentage,intersection" ).split( ',' );
40}
41
42QString QgsCalculateVectorOverlapsAlgorithm::group() const
43{
44 return QObject::tr( "Vector analysis" );
45}
46
47QString QgsCalculateVectorOverlapsAlgorithm::groupId() const
48{
49 return QStringLiteral( "vectoranalysis" );
50}
51
52void QgsCalculateVectorOverlapsAlgorithm::initAlgorithm( const QVariantMap & )
53{
54 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) ) );
55 addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Overlay layers" ), Qgis::ProcessingSourceType::VectorPolygon ) );
56 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Overlap" ) ) );
57
58 auto gridSize = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "GRID_SIZE" ), QObject::tr( "Grid size" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true, 0 );
59 gridSize->setFlags( gridSize->flags() | Qgis::ProcessingParameterFlag::Advanced );
60 addParameter( gridSize.release() );
61}
62
63QIcon QgsCalculateVectorOverlapsAlgorithm::icon() const
64{
65 return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmClip.svg" ) );
66}
67
68QString QgsCalculateVectorOverlapsAlgorithm::svgIconPath() const
69{
70 return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmClip.svg" ) );
71}
72
73QString QgsCalculateVectorOverlapsAlgorithm::shortHelpString() const
74{
75 return QObject::tr( "This algorithm calculates the area and percentage cover by which features from an input layer "
76 "are overlapped by features from a selection of overlay layers.\n\n"
77 "New attributes are added to the output layer reporting the total area of overlap and percentage of the input feature overlapped "
78 "by each of the selected overlay layers." );
79}
80
81QString QgsCalculateVectorOverlapsAlgorithm::shortDescription() const
82{
83 return QObject::tr( "Calculates the area and percentage cover by which features from an input layer "
84 "are overlapped by features from a selection of overlay layers." );
85}
86
87Qgis::ProcessingAlgorithmDocumentationFlags QgsCalculateVectorOverlapsAlgorithm::documentationFlags() const
88{
90}
91
92QgsCalculateVectorOverlapsAlgorithm *QgsCalculateVectorOverlapsAlgorithm::createInstance() const
93{
94 return new QgsCalculateVectorOverlapsAlgorithm();
95}
96
97bool QgsCalculateVectorOverlapsAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
98{
99 mSource.reset( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
100 if ( !mSource )
101 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
102
103 mOutputFields = mSource->fields();
104
105 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
106 mOverlayerSources.reserve( layers.size() );
107 mLayerNames.reserve( layers.size() );
108 for ( QgsMapLayer *layer : layers )
109 {
110 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
111 {
112 mLayerNames << layer->name();
113 mOverlayerSources.emplace_back( std::make_unique<QgsVectorLayerFeatureSource>( vl ) );
114 QgsFields newFields;
115 newFields.append( QgsField( QStringLiteral( "%1_area" ).arg( vl->name() ), QMetaType::Type::Double ) );
116 newFields.append( QgsField( QStringLiteral( "%1_pc" ).arg( vl->name() ), QMetaType::Type::Double ) );
117 mOutputFields = QgsProcessingUtils::combineFields( mOutputFields, newFields );
118 }
119 }
120
121 mOutputType = mSource->wkbType();
122 mCrs = mSource->sourceCrs();
123 mInputCount = mSource->featureCount();
124 mInputFeatures = mSource->getFeatures();
125 return true;
126}
127
128QVariantMap QgsCalculateVectorOverlapsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
129{
130 QString destId;
131 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, destId, mOutputFields, mOutputType, mCrs ) );
132 if ( !sink )
133 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
134
135 // build a spatial index for each constraint layer for speed. We also store input constraint geometries here,
136 // to avoid refetching and projecting them later
137 QList<QgsSpatialIndex> spatialIndices;
138 spatialIndices.reserve( mLayerNames.size() );
139 auto nameIt = mLayerNames.constBegin();
140 for ( auto sourceIt = mOverlayerSources.begin(); sourceIt != mOverlayerSources.end(); ++sourceIt, ++nameIt )
141 {
142 feedback->pushInfo( QObject::tr( "Preparing %1" ).arg( *nameIt ) );
143 const QgsFeatureIterator featureIt = ( *sourceIt )->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( mCrs, context.transformContext() ).setInvalidGeometryCheck( context.invalidGeometryCheck() ).setInvalidGeometryCallback( context.invalidGeometryCallback() ) );
144 spatialIndices << QgsSpatialIndex( featureIt, feedback, QgsSpatialIndex::FlagStoreFeatureGeometries );
145 }
146
148 da.setSourceCrs( mCrs, context.transformContext() );
149 da.setEllipsoid( context.ellipsoid() );
150
151 QgsGeometryParameters geometryParameters;
152 if ( parameters.value( QStringLiteral( "GRID_SIZE" ) ).isValid() )
153 {
154 geometryParameters.setGridSize( parameterAsDouble( parameters, QStringLiteral( "GRID_SIZE" ), context ) );
155 }
156
157 // loop through input
158 const double step = mInputCount > 0 ? 100.0 / mInputCount : 0;
159 long i = 0;
160 QgsFeature feature;
161 while ( mInputFeatures.nextFeature( feature ) )
162 {
163 if ( feedback->isCanceled() )
164 break;
165
166 QgsAttributes outAttributes = feature.attributes();
167 if ( feature.hasGeometry() && !qgsDoubleNear( feature.geometry().area(), 0.0 ) )
168 {
169 const QgsGeometry inputGeom = feature.geometry();
170
171 double inputArea = 0;
172 try
173 {
174 inputArea = da.measureArea( inputGeom );
175 }
176 catch ( QgsCsException & )
177 {
178 throw QgsProcessingException( QObject::tr( "An error occurred while calculating feature area" ) );
179 }
180
181 // prepare for lots of intersection tests (for speed)
182 std::unique_ptr<QgsGeometryEngine> bufferGeomEngine( QgsGeometry::createGeometryEngine( inputGeom.constGet() ) );
183 bufferGeomEngine->prepareGeometry();
184
185 // calculate overlap attributes
186 auto spatialIteratorIt = spatialIndices.begin();
187 for ( auto it = mOverlayerSources.begin(); it != mOverlayerSources.end(); ++it, ++spatialIteratorIt )
188 {
189 if ( feedback->isCanceled() )
190 break;
191
192 const QgsSpatialIndex &index = *spatialIteratorIt;
193 const QList<QgsFeatureId> matches = index.intersects( inputGeom.boundingBox() );
194 QVector<QgsGeometry> intersectingGeoms;
195 intersectingGeoms.reserve( matches.count() );
196 for ( const QgsFeatureId match : matches )
197 {
198 if ( feedback->isCanceled() )
199 break;
200
201 const QgsGeometry overlayGeometry = index.geometry( match );
202 if ( bufferGeomEngine->intersects( overlayGeometry.constGet() ) )
203 {
204 intersectingGeoms.append( overlayGeometry );
205 }
206 }
207
208 if ( feedback->isCanceled() )
209 break;
210
211 // dissolve intersecting features, calculate total area of them within our buffer
212 const QgsGeometry overlayDissolved = QgsGeometry::unaryUnion( intersectingGeoms, geometryParameters );
213
214 if ( feedback->isCanceled() )
215 break;
216
217 const QgsGeometry overlayIntersection = inputGeom.intersection( overlayDissolved, geometryParameters );
218
219 double overlayArea = 0;
220 try
221 {
222 overlayArea = da.measureArea( overlayIntersection );
223 }
224 catch ( QgsCsException & )
225 {
226 throw QgsProcessingException( QObject::tr( "An error occurred while calculating feature area" ) );
227 }
228
229 outAttributes.append( overlayArea );
230 outAttributes.append( 100 * overlayArea / inputArea );
231 }
232 }
233 else
234 {
235 // input feature has no geometry
236 for ( auto it = mOverlayerSources.begin(); it != mOverlayerSources.end(); ++it )
237 {
238 outAttributes.append( QVariant() );
239 outAttributes.append( QVariant() );
240 }
241 }
242
243 feature.setAttributes( outAttributes );
244 if ( !sink->addFeature( feature, QgsFeatureSink::FastInsert ) )
245 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
246
247 i++;
248 feedback->setProgress( i * step );
249 }
250
251 sink->finalize();
252
253 QVariantMap outputs;
254 outputs.insert( QStringLiteral( "OUTPUT" ), destId );
255 return outputs;
256}
257
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3536
@ RespectsEllipsoid
Algorithm respects the context's ellipsoid settings, and uses ellipsoidal based measurements.
Definition qgis.h:3621
QFlags< ProcessingAlgorithmDocumentationFlag > ProcessingAlgorithmDocumentationFlags
Flags describing algorithm behavior for documentation purposes.
Definition qgis.h:3630
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3763
@ Double
Double/float values.
Definition qgis.h:3804
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A vector of attributes.
Custom exception class for Coordinate Reference System related exceptions.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
double measureArea(const QgsGeometry &geometry) const
Measures the area of a geometry.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
Wrapper for iterator of features from vector data provider or vector layer.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setInvalidGeometryCheck(Qgis::InvalidGeometryCheck check)
Sets invalid geometry checking behavior.
QgsFeatureRequest & setInvalidGeometryCallback(const std::function< void(const QgsFeature &)> &callback)
Sets a callback function to use when encountering an invalid geometry and invalidGeometryCheck() is s...
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsAttributes attributes
Definition qgsfeature.h:67
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:54
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:73
Encapsulates parameters under which a geometry operation is performed.
void setGridSize(double size)
Sets the grid size which will be used to snap vertices of a geometry.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
double area() const
Returns the planar, 2-dimensional area of the geometry.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Base class for all map layer types.
Definition qgsmaplayer.h:80
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
std::function< void(const QgsFeature &) > invalidGeometryCallback(QgsFeatureSource *source=nullptr) const
Returns the callback function to use when encountering an invalid geometry and invalidGeometryCheck()...
QString ellipsoid() const
Returns the ellipsoid to use for distance and area calculations.
Qgis::InvalidGeometryCheck invalidGeometryCheck() const
Returns the behavior used for checking invalid geometries in input layers.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
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 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).
A spatial index for QgsFeature objects.
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
QgsGeometry geometry(QgsFeatureId id) const
Returns the stored geometry for the indexed feature with matching id.
Represents a vector layer which manages a vector based dataset.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:28