Skip to content

Commit

Permalink
Merge pull request #4858 from nyalldawson/points
Browse files Browse the repository at this point in the history
Port Points in Polygon algorithms to new API
  • Loading branch information
nyalldawson committed Jul 15, 2017
2 parents 455769c + 79df6b4 commit 2230597
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 381 deletions.
14 changes: 3 additions & 11 deletions python/plugins/processing/algs/help/qgis.yaml
Expand Up @@ -75,19 +75,11 @@ qgis:countpointsinpolygon: >

A new polygons layer is generated, with the exact same content as the input polygons layer, but containing an additional field with the points count corresponding to each polygon.

qgis:countpointsinpolygonweighted: >
This algorithm takes a points layer and a polygon layer and counts the number of points from the first one in each polygon of the second one.
An optional weight field can be used to assign weights to each point. If set, the count generated will be the sum of the weight field for each point contained by the polygon.

An attribute is used in the points layer to assign weights to each point.
Alternatively, a unique class field can be specified. If set, points are classified based on the selected attribute, and if several points with the same attribute value are within the polygon, only one of them is counted. The final count of the point in a polygon is, therefore, the count of different classes that are found in it.

A new polygons layer is generated, with the exact same content as the input polygons layer, but containing an additional field with the points count corresponding to each polygon.

qgis:countuniquepointsinpolygon: >
This algorithm takes a points layer and a polygon layer and counts the number of points from the first one in each polygon of the second one.

Points are classified based on an attribute, and if several points with the same attribute value are within the extent of the polygon, only one of them is counted. The final count of point in a polygon is, therefore, the count of different classes that are found in it.

A new polygons layer is generated, with the exact same content as the input polygons layer, but containing an additional field with the points count corresponding to each polygon.
Both the weight field and unique class field cannot be specified. If they are, the weight field will take precedence and the unique class field will be ignored.

qgis:createattributeindex: >
Creates an index to speed up queries made against a field in a table. Support for index creation is dependent on the layer's data provider and the field type.
Expand Down
154 changes: 103 additions & 51 deletions python/plugins/processing/algs/qgis/PointsInPolygon.py
Expand Up @@ -30,23 +30,30 @@
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtCore import QVariant

from qgis.core import QgsGeometry, QgsFeatureSink, QgsFeatureRequest, QgsFeature, QgsField, QgsProcessingUtils
from qgis.core import (QgsGeometry,
QgsFeatureSink,
QgsFeatureRequest,
QgsFeature,
QgsField,
QgsProcessing,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterString,
QgsProcessingParameterField,
QgsSpatialIndex)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterString
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]


class PointsInPolygon(QgisAlgorithm):

POLYGONS = 'POLYGONS'
POINTS = 'POINTS'
OUTPUT = 'OUTPUT'
FIELD = 'FIELD'
WEIGHT = 'WEIGHT'
CLASSFIELD = 'CLASSFIELD'

def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'sum_points.png'))
Expand All @@ -58,13 +65,20 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.POLYGONS,
self.tr('Polygons'), [dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(ParameterVector(self.POINTS,
self.tr('Points'), [dataobjects.TYPE_VECTOR_POINT]))
self.addParameter(ParameterString(self.FIELD,
self.tr('Count field name'), 'NUMPOINTS'))
self.addOutput(OutputVector(self.OUTPUT, self.tr('Count'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSource(self.POLYGONS,
self.tr('Polygons'), [QgsProcessing.TypeVectorPolygon]))
self.addParameter(QgsProcessingParameterFeatureSource(self.POINTS,
self.tr('Points'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(QgsProcessingParameterField(self.WEIGHT,
self.tr('Weight field'), parentLayerParameterName=self.POINTS,
optional=True))
self.addParameter(QgsProcessingParameterField(self.CLASSFIELD,
self.tr('Class field'), parentLayerParameterName=self.POINTS,
optional=True))
self.addParameter(QgsProcessingParameterString(self.FIELD,
self.tr('Count field name'), defaultValue='NUMPOINTS'))
self.addParameter(
QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Count'), QgsProcessing.TypeVectorPolygon))

def name(self):
return 'countpointsinpolygon'
Expand All @@ -73,54 +87,92 @@ def displayName(self):
return self.tr('Count points in polygon')

def processAlgorithm(self, parameters, context, feedback):
polyLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POLYGONS), context)
pointLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POINTS), context)
fieldName = self.getParameterValue(self.FIELD)
poly_source = self.parameterAsSource(parameters, self.POLYGONS, context)
point_source = self.parameterAsSource(parameters, self.POINTS, context)

weight_field = self.parameterAsString(parameters, self.WEIGHT, context)
weight_field_index = -1
if weight_field:
weight_field_index = point_source.fields().lookupField(weight_field)

fields = polyLayer.fields()
fields.append(QgsField(fieldName, QVariant.Int))
class_field = self.parameterAsString(parameters, self.CLASSFIELD, context)
class_field_index = -1
if class_field:
class_field_index = point_source.fields().lookupField(class_field)

(idxCount, fieldList) = vector.findOrCreateField(polyLayer,
polyLayer.fields(), fieldName)
field_name = self.parameterAsString(parameters, self.FIELD, context)

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, polyLayer.wkbType(),
polyLayer.crs(), context)
fields = poly_source.fields()
if fields.lookupField(field_name) < 0:
fields.append(QgsField(field_name, QVariant.Int))
field_index = fields.lookupField(field_name)

spatialIndex = QgsProcessingUtils.createSpatialIndex(pointLayer, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, poly_source.wkbType(), poly_source.sourceCrs())

ftPoly = QgsFeature()
ftPoint = QgsFeature()
outFeat = QgsFeature()
geom = QgsGeometry()
spatialIndex = QgsSpatialIndex(point_source.getFeatures(
QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs())))

features = QgsProcessingUtils.getFeatures(polyLayer, context)
total = 100.0 / polyLayer.featureCount() if polyLayer.featureCount() else 0
for current, ftPoly in enumerate(features):
geom = ftPoly.geometry()
engine = QgsGeometry.createGeometryEngine(geom.geometry())
engine.prepareGeometry()
point_attribute_indices = []
if weight_field_index >= 0:
point_attribute_indices.append(weight_field_index)
if class_field_index >= 0:
point_attribute_indices.append(class_field_index)

attrs = ftPoly.attributes()
features = poly_source.getFeatures()
total = 100.0 / poly_source.featureCount() if poly_source.featureCount() else 0
for current, polygon_feature in enumerate(features):
if feedback.isCanceled():
break

count = 0
points = spatialIndex.intersects(geom.boundingBox())
if len(points) > 0:
request = QgsFeatureRequest().setFilterFids(points).setSubsetOfAttributes([])
fit = pointLayer.getFeatures(request)
ftPoint = QgsFeature()
while fit.nextFeature(ftPoint):
tmpGeom = ftPoint.geometry()
if engine.contains(tmpGeom.geometry()):
count += 1

outFeat.setGeometry(geom)
if idxCount == len(attrs):
attrs.append(count)
output_feature = QgsFeature()
if polygon_feature.hasGeometry():
geom = polygon_feature.geometry()
engine = QgsGeometry.createGeometryEngine(geom.geometry())
engine.prepareGeometry()

count = 0
classes = set()

points = spatialIndex.intersects(geom.boundingBox())
if len(points) > 0:
request = QgsFeatureRequest().setFilterFids(points).setDestinationCrs(poly_source.sourceCrs())
request.setSubsetOfAttributes(point_attribute_indices)
for point_feature in point_source.getFeatures(request):
if feedback.isCanceled():
break

if engine.contains(point_feature.geometry().geometry()):
if weight_field_index >= 0:
weight = point_feature.attributes()[weight_field_index]
try:
count += float(weight)
except:
# Ignore fields with non-numeric values
pass
elif class_field_index >= 0:
point_class = point_feature.attributes()[class_field_index]
if point_class not in classes:
classes.add(point_class)
else:
count += 1

output_feature.setGeometry(geom)

attrs = polygon_feature.attributes()

if class_field_index >= 0:
score = len(classes)
else:
score = count
if field_index == len(attrs):
attrs.append(score)
else:
attrs[idxCount] = count
outFeat.setAttributes(attrs)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
attrs[field_index] = score
output_feature.setAttributes(attrs)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)

feedback.setProgress(int(current * total))

del writer
return {self.OUTPUT: dest_id}
129 changes: 0 additions & 129 deletions python/plugins/processing/algs/qgis/PointsInPolygonUnique.py

This file was deleted.

0 comments on commit 2230597

Please sign in to comment.