QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
20#include "qgsapplication.h"
21#include "qgsgeometryengine.h"
22#include "qgsprocessing.h"
23#include "qgsvectorlayer.h"
24
26
27void QgsPointsInPolygonAlgorithm::initParameters( const QVariantMap &configuration )
28{
29 mIsInPlace = configuration.value( QStringLiteral( "IN_PLACE" ) ).toBool();
30
31 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "POINTS" ), QObject::tr( "Points" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPoint ) ) );
32 addParameter( new QgsProcessingParameterField( QStringLiteral( "WEIGHT" ), QObject::tr( "Weight field" ), QVariant(), QStringLiteral( "POINTS" ), Qgis::ProcessingFieldParameterDataType::Any, false, true ) );
33 addParameter( new QgsProcessingParameterField( QStringLiteral( "CLASSFIELD" ), QObject::tr( "Class field" ), QVariant(), QStringLiteral( "POINTS" ), Qgis::ProcessingFieldParameterDataType::Any, false, true ) );
34 if ( mIsInPlace )
35 {
36 addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Count field" ), QStringLiteral( "NUMPOINTS" ), inputParameterName() ) );
37 }
38 else
39 {
40 addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD" ), QObject::tr( "Count field name" ), QStringLiteral( "NUMPOINTS" ) ) );
41 }
42}
43
44QString QgsPointsInPolygonAlgorithm::name() const
45{
46 return QStringLiteral( "countpointsinpolygon" );
47}
48
49QString QgsPointsInPolygonAlgorithm::displayName() const
50{
51 return QObject::tr( "Count points in polygon" );
52}
53
54QStringList QgsPointsInPolygonAlgorithm::tags() const
55{
56 return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
57}
58
59QString QgsPointsInPolygonAlgorithm::svgIconPath() const
60{
61 return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
62}
63
64QIcon QgsPointsInPolygonAlgorithm::icon() const
65{
66 return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
67}
68
69QString QgsPointsInPolygonAlgorithm::group() const
70{
71 return QObject::tr( "Vector analysis" );
72}
73
74QString QgsPointsInPolygonAlgorithm::groupId() const
75{
76 return QStringLiteral( "vectoranalysis" );
77}
78
79QString QgsPointsInPolygonAlgorithm::shortHelpString() const
80{
81 return QObject::tr( "This algorithm takes a points layer and a polygon layer and counts the number of points from "
82 "the first one in each polygons of the second one.\n\n"
83 "A new polygons layer is generated, with the exact same content as the input polygons layer, but "
84 "containing an additional field with the points count corresponding to each polygon.\n\n"
85 "An optional weight field can be used to assign weights to each point. If set, the count generated "
86 "will be the sum of the weight field for each point contained by the polygon.\n\n"
87 "Alternatively, a unique class field can be specified. If set, points are classified based on "
88 "the selected attribute, and if several points with the same attribute value are within the polygon, "
89 "only one of them is counted. The final count of the point in a polygon is, therefore, the count of "
90 "different classes that are found in it.\n\n"
91 "Both the weight field and unique class field cannot be specified. If they are, the weight field will "
92 "take precedence and the unique class field will be ignored." );
93}
94
95QString QgsPointsInPolygonAlgorithm::shortDescription() const
96{
97 return QObject::tr( "Counts point features located within polygon features." );
98}
99
100QgsPointsInPolygonAlgorithm *QgsPointsInPolygonAlgorithm::createInstance() const
101{
102 return new QgsPointsInPolygonAlgorithm();
103}
104
105QList<int> QgsPointsInPolygonAlgorithm::inputLayerTypes() const
106{
107 return QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon );
108}
109
110Qgis::ProcessingSourceType QgsPointsInPolygonAlgorithm::outputLayerType() const
111{
113}
114
115QgsCoordinateReferenceSystem QgsPointsInPolygonAlgorithm::outputCrs( const QgsCoordinateReferenceSystem &inputCrs ) const
116{
117 mCrs = inputCrs;
118 return mCrs;
119}
120
121QString QgsPointsInPolygonAlgorithm::inputParameterName() const
122{
123 return QStringLiteral( "POLYGONS" );
124}
125
126QString QgsPointsInPolygonAlgorithm::inputParameterDescription() const
127{
128 return QObject::tr( "Polygons" );
129}
130
131QString QgsPointsInPolygonAlgorithm::outputName() const
132{
133 return QObject::tr( "Count" );
134}
135
136bool QgsPointsInPolygonAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
137{
138 mFieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
139 mWeightFieldName = parameterAsString( parameters, QStringLiteral( "WEIGHT" ), context );
140 mClassFieldName = parameterAsString( parameters, QStringLiteral( "CLASSFIELD" ), context );
141 mPointSource.reset( parameterAsSource( parameters, QStringLiteral( "POINTS" ), context ) );
142 if ( !mPointSource )
143 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "POINTS" ) ) );
144
145 if ( !mWeightFieldName.isEmpty() )
146 {
147 mWeightFieldIndex = mPointSource->fields().lookupField( mWeightFieldName );
148 if ( mWeightFieldIndex == -1 )
149 throw QgsProcessingException( QObject::tr( "Could not find field %1" ).arg( mWeightFieldName ) );
150 mPointAttributes.append( mWeightFieldIndex );
151 }
152
153 if ( !mClassFieldName.isEmpty() )
154 {
155 mClassFieldIndex = mPointSource->fields().lookupField( mClassFieldName );
156 if ( mClassFieldIndex == -1 )
157 throw QgsProcessingException( QObject::tr( "Could not find field %1" ).arg( mClassFieldIndex ) );
158 mPointAttributes.append( mClassFieldIndex );
159 }
160
161 if ( mPointSource->hasSpatialIndex() == Qgis::SpatialIndexPresence::NotPresent )
162 feedback->pushWarning( QObject::tr( "No spatial index exists for points layer, performance will be severely degraded" ) );
163
164 return true;
165}
166
167QgsFeatureList QgsPointsInPolygonAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
168{
169 QgsFeature outputFeature = feature;
170 if ( !feature.hasGeometry() )
171 {
172 QgsAttributes attrs = feature.attributes();
173 if ( mDestFieldIndex < 0 )
174 attrs.append( 0 );
175 else
176 attrs[mDestFieldIndex] = 0;
177 outputFeature.setAttributes( attrs );
178 return QList<QgsFeature>() << outputFeature;
179 }
180 else
181 {
182 const QgsGeometry polyGeom = feature.geometry();
183 std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( polyGeom.constGet() ) );
184 engine->prepareGeometry();
185
186 double count = 0;
187 QSet<QVariant> classes;
188
190 req.setSubsetOfAttributes( mPointAttributes );
191 QgsFeatureIterator it = mPointSource->getFeatures( req );
192
193 bool ok = false;
194 QgsFeature pointFeature;
195 while ( it.nextFeature( pointFeature ) )
196 {
197 if ( feedback->isCanceled() )
198 break;
199
200 if ( engine->contains( pointFeature.geometry().constGet() ) )
201 {
202 if ( mWeightFieldIndex >= 0 )
203 {
204 const QVariant weight = pointFeature.attribute( mWeightFieldIndex );
205 const double pointWeight = weight.toDouble( &ok );
206 // Ignore fields with non-numeric values
207 if ( ok )
208 count += pointWeight;
209 else
210 feedback->reportError( QObject::tr( "Weight field value “%1” is not a numeric value" ).arg( weight.toString() ) );
211 }
212 else if ( mClassFieldIndex >= 0 )
213 {
214 const QVariant pointClass = pointFeature.attribute( mClassFieldIndex );
215 classes.insert( pointClass );
216 }
217 else
218 {
219 count++;
220 }
221 }
222 }
223
224 QgsAttributes attrs = feature.attributes();
225 double score = 0;
226
227 if ( mClassFieldIndex >= 0 )
228 score = classes.size();
229 else
230 score = count;
231
232 if ( mDestFieldIndex < 0 )
233 attrs.append( score );
234 else
235 attrs[mDestFieldIndex] = score;
236
237 outputFeature.setAttributes( attrs );
238 return QList<QgsFeature>() << outputFeature;
239 }
240}
241
242QgsFields QgsPointsInPolygonAlgorithm::outputFields( const QgsFields &inputFields ) const
243{
244 if ( mIsInPlace )
245 {
246 mDestFieldIndex = inputFields.lookupField( mFieldName );
247 return inputFields;
248 }
249 else
250 {
251 QgsFields outFields = inputFields;
252 mDestFieldIndex = inputFields.lookupField( mFieldName );
253 if ( mDestFieldIndex < 0 )
254 outFields.append( QgsField( mFieldName, QMetaType::Type::Double ) );
255
256 mFields = outFields;
257 return outFields;
258 }
259}
260
261bool QgsPointsInPolygonAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
262{
263 if ( const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( layer ) )
264 {
265 return vl->geometryType() == Qgis::GeometryType::Polygon;
266 }
267 return false;
268}
269
270
ProcessingSourceType
Processing data source types.
Definition qgis.h:3531
@ VectorPoint
Vector point layers.
Definition qgis.h:3534
@ VectorPolygon
Vector polygon layers.
Definition qgis.h:3536
@ NotPresent
No spatial index exists for the source.
Definition qgis.h:560
@ Polygon
Polygons.
Definition qgis.h:361
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.
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.
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: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.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
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: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
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
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.
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.
Custom exception class for processing related exceptions.
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 dataset.
QList< QgsFeature > QgsFeatureList