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