QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsalgorithmpointsinpolygon.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmpointsinpolygon.cpp
3  ---------------------
4  begin : November 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 "qgsprocessing.h"
20 #include "qgsgeometryengine.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsapplication.h"
23 
25 
26 void QgsPointsInPolygonAlgorithm::initParameters( const QVariantMap &configuration )
27 {
28  mIsInPlace = configuration.value( QStringLiteral( "IN_PLACE" ) ).toBool();
29 
30  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "POINTS" ),
31  QObject::tr( "Points" ), QList< int > () << QgsProcessing::TypeVectorPoint ) );
32  addParameter( new QgsProcessingParameterField( QStringLiteral( "WEIGHT" ),
33  QObject::tr( "Weight field" ), QVariant(), QStringLiteral( "POINTS" ), QgsProcessingParameterField::Any, false, true ) );
34  addParameter( new QgsProcessingParameterField( QStringLiteral( "CLASSFIELD" ),
35  QObject::tr( "Class field" ), QVariant(), QStringLiteral( "POINTS" ), QgsProcessingParameterField::Any, false, true ) );
36  if ( mIsInPlace )
37  {
38  addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ),
39  QObject::tr( "Count field" ), QStringLiteral( "NUMPOINTS" ), inputParameterName() ) );
40  }
41  else
42  {
43  addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD" ),
44  QObject::tr( "Count field name" ), QStringLiteral( "NUMPOINTS" ) ) );
45  }
46 }
47 
48 QString QgsPointsInPolygonAlgorithm::name() const
49 {
50  return QStringLiteral( "countpointsinpolygon" );
51 }
52 
53 QString QgsPointsInPolygonAlgorithm::displayName() const
54 {
55  return QObject::tr( "Count points in polygon" );
56 }
57 
58 QStringList QgsPointsInPolygonAlgorithm::tags() const
59 {
60  return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
61 }
62 
63 QString QgsPointsInPolygonAlgorithm::svgIconPath() const
64 {
65  return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
66 }
67 
68 QIcon QgsPointsInPolygonAlgorithm::icon() const
69 {
70  return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
71 }
72 
73 QString QgsPointsInPolygonAlgorithm::group() const
74 {
75  return QObject::tr( "Vector analysis" );
76 }
77 
78 QString QgsPointsInPolygonAlgorithm::groupId() const
79 {
80  return QStringLiteral( "vectoranalysis" );
81 }
82 
83 QString QgsPointsInPolygonAlgorithm::shortHelpString() const
84 {
85  return QObject::tr( "This algorithm takes a points layer and a polygon layer and counts the number of points from "
86  "the first one in each polygons of the second one.\n\n"
87  "A new polygons layer is generated, with the exact same content as the input polygons layer, but "
88  "containing an additional field with the points count corresponding to each polygon.\n\n"
89  "An optional weight field can be used to assign weights to each point. If set, the count generated "
90  "will be the sum of the weight field for each point contained by the polygon.\n\n"
91  "Alternatively, a unique class field can be specified. If set, points are classified based on "
92  "the selected attribute, and if several points with the same attribute value are within the polygon, "
93  "only one of them is counted. The final count of the point in a polygon is, therefore, the count of "
94  "different classes that are found in it.\n\n"
95  "Both the weight field and unique class field cannot be specified. If they are, the weight field will "
96  "take precedence and the unique class field will be ignored." );
97 }
98 
99 QString QgsPointsInPolygonAlgorithm::shortDescription() const
100 {
101  return QObject::tr( "Counts point features located within polygon features." );
102 }
103 
104 QgsPointsInPolygonAlgorithm *QgsPointsInPolygonAlgorithm::createInstance() const
105 {
106  return new QgsPointsInPolygonAlgorithm();
107 }
108 
109 QList<int> QgsPointsInPolygonAlgorithm::inputLayerTypes() const
110 {
111  return QList< int >() << QgsProcessing::TypeVectorPolygon;
112 }
113 
114 QgsProcessing::SourceType QgsPointsInPolygonAlgorithm::outputLayerType() const
115 {
117 }
118 
120 {
121  mCrs = inputCrs;
122  return mCrs;
123 }
124 
125 QString QgsPointsInPolygonAlgorithm::inputParameterName() const
126 {
127  return QStringLiteral( "POLYGONS" );
128 }
129 
130 QString QgsPointsInPolygonAlgorithm::inputParameterDescription() const
131 {
132  return QObject::tr( "Polygons" );
133 }
134 
135 QString QgsPointsInPolygonAlgorithm::outputName() const
136 {
137  return QObject::tr( "Count" );
138 }
139 
140 bool QgsPointsInPolygonAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
141 {
142  mFieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
143  mWeightFieldName = parameterAsString( parameters, QStringLiteral( "WEIGHT" ), context );
144  mClassFieldName = parameterAsString( parameters, QStringLiteral( "CLASSFIELD" ), context );
145  mPointSource.reset( parameterAsSource( parameters, QStringLiteral( "POINTS" ), context ) );
146  if ( !mPointSource )
147  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "POINTS" ) ) );
148 
149  if ( !mWeightFieldName.isEmpty() )
150  {
151  mWeightFieldIndex = mPointSource->fields().lookupField( mWeightFieldName );
152  if ( mWeightFieldIndex == -1 )
153  throw QgsProcessingException( QObject::tr( "Could not find field %1" ).arg( mWeightFieldName ) );
154  mPointAttributes.append( mWeightFieldIndex );
155  }
156 
157  if ( !mClassFieldName.isEmpty() )
158  {
159  mClassFieldIndex = mPointSource->fields().lookupField( mClassFieldName );
160  if ( mClassFieldIndex == -1 )
161  throw QgsProcessingException( QObject::tr( "Could not find field %1" ).arg( mClassFieldIndex ) );
162  mPointAttributes.append( mClassFieldIndex );
163  }
164 
165  if ( mPointSource->hasSpatialIndex() == QgsFeatureSource::SpatialIndexNotPresent )
166  feedback->pushWarning( QObject::tr( "No spatial index exists for points layer, performance will be severely degraded" ) );
167 
168  return true;
169 }
170 
171 QgsFeatureList QgsPointsInPolygonAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
172 {
173  QgsFeature outputFeature = feature;
174  if ( !feature.hasGeometry() )
175  {
176  QgsAttributes attrs = feature.attributes();
177  if ( mDestFieldIndex < 0 )
178  attrs.append( 0 );
179  else
180  attrs[mDestFieldIndex] = 0;
181  outputFeature.setAttributes( attrs );
182  return QList< QgsFeature > () << outputFeature;
183  }
184  else
185  {
186  const QgsGeometry polyGeom = feature.geometry();
187  std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( polyGeom.constGet() ) );
188  engine->prepareGeometry();
189 
190  double count = 0;
191  QSet< QVariant> classes;
192 
194  req.setSubsetOfAttributes( mPointAttributes );
195  QgsFeatureIterator it = mPointSource->getFeatures( req );
196 
197  bool ok = false;
198  QgsFeature pointFeature;
199  while ( it.nextFeature( pointFeature ) )
200  {
201  if ( feedback->isCanceled() )
202  break;
203 
204  if ( engine->contains( pointFeature.geometry().constGet() ) )
205  {
206  if ( mWeightFieldIndex >= 0 )
207  {
208  const QVariant weight = pointFeature.attribute( mWeightFieldIndex );
209  double pointWeight = weight.toDouble( &ok );
210  // Ignore fields with non-numeric values
211  if ( ok )
212  count += pointWeight;
213  else
214  feedback->reportError( QObject::tr( "Weight field value “%1” is not a numeric value" ).arg( weight.toString() ) );
215  }
216  else if ( mClassFieldIndex >= 0 )
217  {
218  const QVariant pointClass = pointFeature.attribute( mClassFieldIndex );
219  classes.insert( pointClass );
220  }
221  else
222  {
223  count++;
224  }
225  }
226  }
227 
228  QgsAttributes attrs = feature.attributes();
229  double score = 0;
230 
231  if ( mClassFieldIndex >= 0 )
232  score = classes.size();
233  else
234  score = count;
235 
236  if ( mDestFieldIndex < 0 )
237  attrs.append( score );
238  else
239  attrs[mDestFieldIndex] = score;
240 
241  outputFeature.setAttributes( attrs );
242  return QList< QgsFeature >() << outputFeature;
243  }
244 }
245 
246 QgsFields QgsPointsInPolygonAlgorithm::outputFields( const QgsFields &inputFields ) const
247 {
248  if ( mIsInPlace )
249  {
250  mDestFieldIndex = inputFields.lookupField( mFieldName );
251  return inputFields;
252  }
253  else
254  {
255  QgsFields outFields = inputFields;
256  mDestFieldIndex = inputFields.lookupField( mFieldName );
257  if ( mDestFieldIndex < 0 )
258  outFields.append( QgsField( mFieldName, QVariant::Double ) );
259 
260  mFields = outFields;
261  return outFields;
262  }
263 }
264 
265 bool QgsPointsInPolygonAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
266 {
267  if ( const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer ) )
268  {
269  return vl->geometryType() == QgsWkbTypes::PolygonGeometry;
270  }
271  return false;
272 }
273 
274 
276 
277 
278 
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
This class represents a coordinate reference system (CRS).
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
@ SpatialIndexNotPresent
No spatial index exists for the source.
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:135
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:205
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:302
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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:70
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
A string parameter for processing algorithms.
SourceType
Data source types enum.
Definition: qgsprocessing.h:46
@ TypeVectorPolygon
Vector polygon layers.
Definition: qgsprocessing.h:51
@ TypeVectorPoint
Vector point layers.
Definition: qgsprocessing.h:49
Represents a vector layer which manages a vector based data sets.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:736
const QgsCoordinateReferenceSystem & outputCrs