Branch data Line data Source code
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 : :
18 : : #include "qgsalgorithmpointsinpolygon.h"
19 : : #include "qgsprocessing.h"
20 : : #include "qgsgeometryengine.h"
21 : : #include "qgsvectorlayer.h"
22 : : #include "qgsapplication.h"
23 : :
24 : : ///@cond PRIVATE
25 : :
26 : 0 : void QgsPointsInPolygonAlgorithm::initParameters( const QVariantMap &configuration )
27 : : {
28 : 0 : mIsInPlace = configuration.value( QStringLiteral( "IN_PLACE" ) ).toBool();
29 : :
30 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "POINTS" ),
31 : 0 : QObject::tr( "Points" ), QList< int > () << QgsProcessing::TypeVectorPoint ) );
32 : 0 : addParameter( new QgsProcessingParameterField( QStringLiteral( "WEIGHT" ),
33 : 0 : QObject::tr( "Weight field" ), QVariant(), QStringLiteral( "POINTS" ), QgsProcessingParameterField::Any, false, true ) );
34 : 0 : addParameter( new QgsProcessingParameterField( QStringLiteral( "CLASSFIELD" ),
35 : 0 : QObject::tr( "Class field" ), QVariant(), QStringLiteral( "POINTS" ), QgsProcessingParameterField::Any, false, true ) );
36 : 0 : if ( mIsInPlace )
37 : : {
38 : 0 : addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ),
39 : 0 : QObject::tr( "Count field" ), QStringLiteral( "NUMPOINTS" ), inputParameterName() ) );
40 : 0 : }
41 : : else
42 : : {
43 : 0 : addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD" ),
44 : 0 : QObject::tr( "Count field name" ), QStringLiteral( "NUMPOINTS" ) ) );
45 : : }
46 : 0 : }
47 : :
48 : 0 : QString QgsPointsInPolygonAlgorithm::name() const
49 : : {
50 : 0 : return QStringLiteral( "countpointsinpolygon" );
51 : : }
52 : :
53 : 0 : QString QgsPointsInPolygonAlgorithm::displayName() const
54 : : {
55 : 0 : return QObject::tr( "Count points in polygon" );
56 : : }
57 : :
58 : 0 : QStringList QgsPointsInPolygonAlgorithm::tags() const
59 : : {
60 : 0 : return QObject::tr( "extract,filter,intersects,intersecting,disjoint,touching,within,contains,overlaps,relation" ).split( ',' );
61 : 0 : }
62 : :
63 : 0 : QString QgsPointsInPolygonAlgorithm::svgIconPath() const
64 : : {
65 : 0 : return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
66 : 0 : }
67 : :
68 : 0 : QIcon QgsPointsInPolygonAlgorithm::icon() const
69 : : {
70 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmSumPoints.svg" ) );
71 : 0 : }
72 : :
73 : 0 : QString QgsPointsInPolygonAlgorithm::group() const
74 : : {
75 : 0 : return QObject::tr( "Vector analysis" );
76 : : }
77 : :
78 : 0 : QString QgsPointsInPolygonAlgorithm::groupId() const
79 : : {
80 : 0 : return QStringLiteral( "vectoranalysis" );
81 : : }
82 : :
83 : 0 : QString QgsPointsInPolygonAlgorithm::shortHelpString() const
84 : : {
85 : 0 : 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 : 0 : QString QgsPointsInPolygonAlgorithm::shortDescription() const
100 : : {
101 : 0 : return QObject::tr( "Counts point features located within polygon features." );
102 : : }
103 : :
104 : 0 : QgsPointsInPolygonAlgorithm *QgsPointsInPolygonAlgorithm::createInstance() const
105 : : {
106 : 0 : return new QgsPointsInPolygonAlgorithm();
107 : 0 : }
108 : :
109 : 0 : QList<int> QgsPointsInPolygonAlgorithm::inputLayerTypes() const
110 : : {
111 : 0 : return QList< int >() << QgsProcessing::TypeVectorPolygon;
112 : 0 : }
113 : :
114 : 0 : QgsProcessing::SourceType QgsPointsInPolygonAlgorithm::outputLayerType() const
115 : : {
116 : 0 : return QgsProcessing::TypeVectorPolygon;
117 : : }
118 : :
119 : 0 : QgsCoordinateReferenceSystem QgsPointsInPolygonAlgorithm::outputCrs( const QgsCoordinateReferenceSystem &inputCrs ) const
120 : : {
121 : 0 : mCrs = inputCrs;
122 : 0 : return mCrs;
123 : : }
124 : :
125 : 0 : QString QgsPointsInPolygonAlgorithm::inputParameterName() const
126 : : {
127 : 0 : return QStringLiteral( "POLYGONS" );
128 : : }
129 : :
130 : 0 : QString QgsPointsInPolygonAlgorithm::inputParameterDescription() const
131 : : {
132 : 0 : return QObject::tr( "Polygons" );
133 : : }
134 : :
135 : 0 : QString QgsPointsInPolygonAlgorithm::outputName() const
136 : : {
137 : 0 : return QObject::tr( "Count" );
138 : : }
139 : :
140 : 0 : bool QgsPointsInPolygonAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
141 : : {
142 : 0 : mFieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
143 : 0 : mWeightFieldName = parameterAsString( parameters, QStringLiteral( "WEIGHT" ), context );
144 : 0 : mClassFieldName = parameterAsString( parameters, QStringLiteral( "CLASSFIELD" ), context );
145 : 0 : mPointSource.reset( parameterAsSource( parameters, QStringLiteral( "POINTS" ), context ) );
146 : 0 : if ( !mPointSource )
147 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "POINTS" ) ) );
148 : :
149 : 0 : if ( !mWeightFieldName.isEmpty() )
150 : : {
151 : 0 : mWeightFieldIndex = mPointSource->fields().lookupField( mWeightFieldName );
152 : 0 : if ( mWeightFieldIndex == -1 )
153 : 0 : throw QgsProcessingException( QObject::tr( "Could not find field %1" ).arg( mWeightFieldName ) );
154 : 0 : mPointAttributes.append( mWeightFieldIndex );
155 : 0 : }
156 : :
157 : 0 : if ( !mClassFieldName.isEmpty() )
158 : : {
159 : 0 : mClassFieldIndex = mPointSource->fields().lookupField( mClassFieldName );
160 : 0 : if ( mClassFieldIndex == -1 )
161 : 0 : throw QgsProcessingException( QObject::tr( "Could not find field %1" ).arg( mClassFieldIndex ) );
162 : 0 : mPointAttributes.append( mClassFieldIndex );
163 : 0 : }
164 : :
165 : 0 : if ( mPointSource->hasSpatialIndex() == QgsFeatureSource::SpatialIndexNotPresent )
166 : 0 : feedback->pushWarning( QObject::tr( "No spatial index exists for points layer, performance will be severely degraded" ) );
167 : :
168 : 0 : return true;
169 : 0 : }
170 : :
171 : 0 : QgsFeatureList QgsPointsInPolygonAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
172 : : {
173 : 0 : QgsFeature outputFeature = feature;
174 : 0 : if ( !feature.hasGeometry() )
175 : : {
176 : 0 : QgsAttributes attrs = feature.attributes();
177 : 0 : if ( mDestFieldIndex < 0 )
178 : 0 : attrs.append( 0 );
179 : : else
180 : 0 : attrs[mDestFieldIndex] = 0;
181 : 0 : outputFeature.setAttributes( attrs );
182 : 0 : return QList< QgsFeature > () << outputFeature;
183 : 0 : }
184 : : else
185 : : {
186 : 0 : const QgsGeometry polyGeom = feature.geometry();
187 : 0 : std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( polyGeom.constGet() ) );
188 : 0 : engine->prepareGeometry();
189 : :
190 : 0 : double count = 0;
191 : 0 : QSet< QVariant> classes;
192 : :
193 : 0 : QgsFeatureRequest req = QgsFeatureRequest().setFilterRect( polyGeom.boundingBox() ).setDestinationCrs( mCrs, context.transformContext() );
194 : 0 : req.setSubsetOfAttributes( mPointAttributes );
195 : 0 : QgsFeatureIterator it = mPointSource->getFeatures( req );
196 : :
197 : 0 : bool ok = false;
198 : 0 : QgsFeature pointFeature;
199 : 0 : while ( it.nextFeature( pointFeature ) )
200 : : {
201 : 0 : if ( feedback->isCanceled() )
202 : 0 : break;
203 : :
204 : 0 : if ( engine->contains( pointFeature.geometry().constGet() ) )
205 : : {
206 : 0 : if ( mWeightFieldIndex >= 0 )
207 : : {
208 : 0 : const QVariant weight = pointFeature.attribute( mWeightFieldIndex );
209 : 0 : double pointWeight = weight.toDouble( &ok );
210 : : // Ignore fields with non-numeric values
211 : 0 : if ( ok )
212 : 0 : count += pointWeight;
213 : : else
214 : 0 : feedback->reportError( QObject::tr( "Weight field value “%1” is not a numeric value" ).arg( weight.toString() ) );
215 : 0 : }
216 : 0 : else if ( mClassFieldIndex >= 0 )
217 : : {
218 : 0 : const QVariant pointClass = pointFeature.attribute( mClassFieldIndex );
219 : 0 : classes.insert( pointClass );
220 : 0 : }
221 : : else
222 : : {
223 : 0 : count++;
224 : : }
225 : 0 : }
226 : : }
227 : :
228 : 0 : QgsAttributes attrs = feature.attributes();
229 : 0 : double score = 0;
230 : :
231 : 0 : if ( mClassFieldIndex >= 0 )
232 : 0 : score = classes.size();
233 : : else
234 : 0 : score = count;
235 : :
236 : 0 : if ( mDestFieldIndex < 0 )
237 : 0 : attrs.append( score );
238 : : else
239 : 0 : attrs[mDestFieldIndex] = score;
240 : :
241 : 0 : outputFeature.setAttributes( attrs );
242 : 0 : return QList< QgsFeature >() << outputFeature;
243 : 0 : }
244 : 0 : }
245 : :
246 : 0 : QgsFields QgsPointsInPolygonAlgorithm::outputFields( const QgsFields &inputFields ) const
247 : : {
248 : 0 : if ( mIsInPlace )
249 : : {
250 : 0 : mDestFieldIndex = inputFields.lookupField( mFieldName );
251 : 0 : return inputFields;
252 : : }
253 : : else
254 : : {
255 : 0 : QgsFields outFields = inputFields;
256 : 0 : mDestFieldIndex = inputFields.lookupField( mFieldName );
257 : 0 : if ( mDestFieldIndex < 0 )
258 : 0 : outFields.append( QgsField( mFieldName, QVariant::Double ) );
259 : :
260 : 0 : mFields = outFields;
261 : 0 : return outFields;
262 : 0 : }
263 : 0 : }
264 : :
265 : 0 : bool QgsPointsInPolygonAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
266 : : {
267 : 0 : if ( const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer ) )
268 : : {
269 : 0 : return vl->geometryType() == QgsWkbTypes::PolygonGeometry;
270 : : }
271 : 0 : return false;
272 : 0 : }
273 : :
274 : :
275 : : ///@endcond
276 : :
277 : :
278 : :
|