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