QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
26void 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 > () << static_cast< int >( Qgis::ProcessingSourceType::VectorPoint ) ) );
32 addParameter( new QgsProcessingParameterField( QStringLiteral( "WEIGHT" ),
33 QObject::tr( "Weight field" ), QVariant(), QStringLiteral( "POINTS" ), Qgis::ProcessingFieldParameterDataType::Any, false, true ) );
34 addParameter( new QgsProcessingParameterField( QStringLiteral( "CLASSFIELD" ),
35 QObject::tr( "Class field" ), QVariant(), QStringLiteral( "POINTS" ), Qgis::ProcessingFieldParameterDataType::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
48QString QgsPointsInPolygonAlgorithm::name() const
49{
50 return QStringLiteral( "countpointsinpolygon" );
51}
52
53QString QgsPointsInPolygonAlgorithm::displayName() const
54{
55 return QObject::tr( "Count points in polygon" );
56}
57
58QStringList QgsPointsInPolygonAlgorithm::tags() const
59{
60 return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
61}
62
63QString QgsPointsInPolygonAlgorithm::svgIconPath() const
64{
65 return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
66}
67
68QIcon QgsPointsInPolygonAlgorithm::icon() const
69{
70 return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
71}
72
73QString QgsPointsInPolygonAlgorithm::group() const
74{
75 return QObject::tr( "Vector analysis" );
76}
77
78QString QgsPointsInPolygonAlgorithm::groupId() const
79{
80 return QStringLiteral( "vectoranalysis" );
81}
82
83QString 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
99QString QgsPointsInPolygonAlgorithm::shortDescription() const
100{
101 return QObject::tr( "Counts point features located within polygon features." );
102}
103
104QgsPointsInPolygonAlgorithm *QgsPointsInPolygonAlgorithm::createInstance() const
105{
106 return new QgsPointsInPolygonAlgorithm();
107}
108
109QList<int> QgsPointsInPolygonAlgorithm::inputLayerTypes() const
110{
111 return QList< int >() << static_cast< int >( Qgis::ProcessingSourceType::VectorPolygon );
112}
113
114Qgis::ProcessingSourceType QgsPointsInPolygonAlgorithm::outputLayerType() const
115{
117}
118
120{
121 mCrs = inputCrs;
122 return mCrs;
123}
124
125QString QgsPointsInPolygonAlgorithm::inputParameterName() const
126{
127 return QStringLiteral( "POLYGONS" );
128}
129
130QString QgsPointsInPolygonAlgorithm::inputParameterDescription() const
131{
132 return QObject::tr( "Polygons" );
133}
134
135QString QgsPointsInPolygonAlgorithm::outputName() const
136{
137 return QObject::tr( "Count" );
138}
139
140bool 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() == Qgis::SpatialIndexPresence::NotPresent )
166 feedback->pushWarning( QObject::tr( "No spatial index exists for points layer, performance will be severely degraded" ) );
167
168 return true;
169}
170
171QgsFeatureList 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 const 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
246QgsFields 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
265bool QgsPointsInPolygonAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
266{
267 if ( const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer ) )
268 {
269 return vl->geometryType() == Qgis::GeometryType::Polygon;
270 }
271 return false;
272}
273
274
276
277
278
ProcessingSourceType
Processing data source types.
Definition: qgis.h:2858
@ VectorPoint
Vector point layers.
@ VectorPolygon
Vector polygon layers.
@ NotPresent
No spatial index exists for the source.
@ Polygon
Polygons.
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:59
This class represents a coordinate reference system (CRS).
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
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.
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:160
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:230
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:335
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
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:359
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:162
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0)
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:75
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.
Represents a vector layer which manages a vector based data sets.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:917
const QgsCoordinateReferenceSystem & outputCrs