QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 #include "qgsvectorlayer.h"
20 #include "qgsgeometryengine.h"
21 #include "qgsdistancearea.h"
22 
24 
25 QString QgsCalculateVectorOverlapsAlgorithm::name() const
26 {
27  return QStringLiteral( "calculatevectoroverlaps" );
28 }
29 
30 QString QgsCalculateVectorOverlapsAlgorithm::displayName() const
31 {
32  return QObject::tr( "Overlap analysis" );
33 }
34 
35 QStringList QgsCalculateVectorOverlapsAlgorithm::tags() const
36 {
37  return QObject::tr( "vector,overlay,area,percentage,intersection" ).split( ',' );
38 }
39 
40 QString QgsCalculateVectorOverlapsAlgorithm::group() const
41 {
42  return QObject::tr( "Vector analysis" );
43 }
44 
45 QString QgsCalculateVectorOverlapsAlgorithm::groupId() const
46 {
47  return QStringLiteral( "vectoranalysis" );
48 }
49 
50 void QgsCalculateVectorOverlapsAlgorithm::initAlgorithm( const QVariantMap & )
51 {
52  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorPolygon ) );
53  addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Overlay layers" ), QgsProcessing::TypeVectorPolygon ) );
54  addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Overlap" ) ) );
55 }
56 
57 QIcon QgsCalculateVectorOverlapsAlgorithm::icon() const
58 {
59  return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmClip.svg" ) );
60 }
61 
62 QString QgsCalculateVectorOverlapsAlgorithm::svgIconPath() const
63 {
64  return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmClip.svg" ) );
65 }
66 
67 QString QgsCalculateVectorOverlapsAlgorithm::shortHelpString() const
68 {
69  return QObject::tr( "This algorithm calculates the area and percentage cover by which features from an input layer "
70  "are overlapped by features from a selection of overlay layers.\n\n"
71  "New attributes are added to the output layer reporting the total area of overlap and percentage of the input feature overlapped "
72  "by each of the selected overlay layers." );
73 }
74 
75 QgsCalculateVectorOverlapsAlgorithm *QgsCalculateVectorOverlapsAlgorithm::createInstance() const
76 {
77  return new QgsCalculateVectorOverlapsAlgorithm();
78 }
79 
80 bool QgsCalculateVectorOverlapsAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
81 {
82  mSource.reset( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
83  if ( !mSource )
84  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
85 
86  mOutputFields = mSource->fields();
87 
88  const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
89  mOverlayerSources.reserve( layers.size() );
90  mLayerNames.reserve( layers.size() );
91  for ( QgsMapLayer *layer : layers )
92  {
93  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
94  {
95  mLayerNames << layer->name();
96  mOverlayerSources.emplace_back( std::make_unique< QgsVectorLayerFeatureSource >( vl ) );
97  mOutputFields.append( QgsField( QStringLiteral( "%1_area" ).arg( vl->name() ), QVariant::Double ) );
98  mOutputFields.append( QgsField( QStringLiteral( "%1_pc" ).arg( vl->name() ), QVariant::Double ) );
99  }
100  }
101 
102  mOutputType = mSource->wkbType();
103  mCrs = mSource->sourceCrs();
104  mInputCount = mSource->featureCount();
105  mInputFeatures = mSource->getFeatures();
106  return true;
107 }
108 
109 QVariantMap QgsCalculateVectorOverlapsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
110 {
111  QString destId;
112  std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, destId, mOutputFields,
113  mOutputType, mCrs ) );
114  if ( !sink )
115  throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
116 
117  // build a spatial index for each constraint layer for speed. We also store input constraint geometries here,
118  // to avoid refetching and projecting them later
119  QList< QgsSpatialIndex > spatialIndices;
120  spatialIndices.reserve( mLayerNames.size() );
121  auto nameIt = mLayerNames.constBegin();
122  for ( auto sourceIt = mOverlayerSources.begin(); sourceIt != mOverlayerSources.end(); ++sourceIt, ++nameIt )
123  {
124  feedback->pushInfo( QObject::tr( "Preparing %1" ).arg( *nameIt ) );
125  const QgsFeatureIterator featureIt = ( *sourceIt )->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ).setDestinationCrs( mCrs, context.transformContext() ).setInvalidGeometryCheck( context.invalidGeometryCheck() ).setInvalidGeometryCallback( context.invalidGeometryCallback() ) );
126  spatialIndices << QgsSpatialIndex( featureIt, feedback, QgsSpatialIndex::FlagStoreFeatureGeometries );
127  }
128 
129  QgsDistanceArea da;
130  da.setSourceCrs( mCrs, context.transformContext() );
131  da.setEllipsoid( context.ellipsoid() );
132 
133  // loop through input
134  const double step = mInputCount > 0 ? 100.0 / mInputCount : 0;
135  long i = 0;
136  QgsFeature feature;
137  while ( mInputFeatures.nextFeature( feature ) )
138  {
139  if ( feedback->isCanceled() )
140  break;
141 
142  QgsAttributes outAttributes = feature.attributes();
143  if ( feature.hasGeometry() && !qgsDoubleNear( feature.geometry().area(), 0.0 ) )
144  {
145  const QgsGeometry inputGeom = feature.geometry();
146  const double inputArea = da.measureArea( inputGeom );
147 
148  // prepare for lots of intersection tests (for speed)
149  std::unique_ptr< QgsGeometryEngine > bufferGeomEngine( QgsGeometry::createGeometryEngine( inputGeom.constGet() ) );
150  bufferGeomEngine->prepareGeometry();
151 
152  // calculate overlap attributes
153  auto spatialIteratorIt = spatialIndices.begin();
154  for ( auto it = mOverlayerSources.begin(); it != mOverlayerSources.end(); ++ it, ++spatialIteratorIt )
155  {
156  if ( feedback->isCanceled() )
157  break;
158 
159  const QgsSpatialIndex &index = *spatialIteratorIt;
160  const QList<QgsFeatureId> matches = index.intersects( inputGeom.boundingBox() );
161  QVector< QgsGeometry > intersectingGeoms;
162  intersectingGeoms.reserve( matches.count() );
163  for ( const QgsFeatureId match : matches )
164  {
165  if ( feedback->isCanceled() )
166  break;
167 
168  const QgsGeometry overlayGeometry = index.geometry( match );
169  if ( bufferGeomEngine->intersects( overlayGeometry.constGet() ) )
170  {
171  intersectingGeoms.append( overlayGeometry );
172  }
173  }
174 
175  if ( feedback->isCanceled() )
176  break;
177 
178  // dissolve intersecting features, calculate total area of them within our buffer
179  const QgsGeometry overlayDissolved = QgsGeometry::unaryUnion( intersectingGeoms );
180 
181  if ( feedback->isCanceled() )
182  break;
183 
184  const QgsGeometry overlayIntersection = inputGeom.intersection( overlayDissolved );
185 
186  const double overlayArea = da.measureArea( overlayIntersection );
187  outAttributes.append( overlayArea );
188  outAttributes.append( 100 * overlayArea / inputArea );
189  }
190  }
191  else
192  {
193  // input feature has no geometry
194  for ( auto it = mOverlayerSources.begin(); it != mOverlayerSources.end(); ++ it )
195  {
196  outAttributes.append( QVariant() );
197  outAttributes.append( QVariant() );
198  }
199  }
200 
201  feature.setAttributes( outAttributes );
202  if ( !sink->addFeature( feature, QgsFeatureSink::FastInsert ) )
203  throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
204 
205  i++;
206  feedback->setProgress( i * step );
207  }
208 
209  QVariantMap outputs;
210  outputs.insert( QStringLiteral( "OUTPUT" ), destId );
211  return outputs;
212 }
213 
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.
Definition: qgsattributes.h:58
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.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setInvalidGeometryCallback(const std::function< void(const QgsFeature &)> &callback)
Sets a callback function to use when encountering an invalid geometry and invalidGeometryCheck() is s...
QgsFeatureRequest & setInvalidGeometryCheck(InvalidGeometryCheck check)
Sets invalid geometry checking behavior.
@ 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:56
QgsAttributes attributes
Definition: qgsfeature.h:65
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:153
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:223
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
double area() const
Returns the planar, 2-dimensional area of the geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
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.
QgsFeatureRequest::InvalidGeometryCheck invalidGeometryCheck() const
Returns the behavior used for checking invalid geometries in input layers.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
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.
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:51
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 data sets.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QList< int > QgsAttributeList
Definition: qgsfield.h:26