Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #4860 from nyalldawson/sum_lines
Port sum line length algorithm to new API
  • Loading branch information
nyalldawson committed Jul 15, 2017
2 parents 2230597 + 2e8b848 commit bbe466f
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 86 deletions.
6 changes: 3 additions & 3 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -79,14 +79,14 @@
from .Smooth import Smooth
from .SnapGeometries import SnapGeometriesToLayer
from .SpatialiteExecuteSQL import SpatialiteExecuteSQL
from .SumLines import SumLines
from .SymmetricalDifference import SymmetricalDifference
from .Union import Union
from .VectorSplit import VectorSplit
from .VoronoiPolygons import VoronoiPolygons
from .ZonalStatistics import ZonalStatistics

# from .ExtractByLocation import ExtractByLocation
# from .SumLines import SumLines
# from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
# from .LinesIntersection import LinesIntersection
# from .MeanCoords import MeanCoords
Expand Down Expand Up @@ -183,8 +183,7 @@ def __init__(self):
self.externalAlgs = []

def getAlgs(self):
# algs = [SumLines(),
# NearestNeighbourAnalysis(), MeanCoords(),
# algs = [NearestNeighbourAnalysis(), MeanCoords(),
# LinesIntersection(), UniqueValues(), PointDistance(),
# ExportGeometryInfo(),
# SinglePartsToMultiparts(),
Expand Down Expand Up @@ -274,6 +273,7 @@ def getAlgs(self):
Smooth(),
SnapGeometriesToLayer(),
SpatialiteExecuteSQL(),
SumLines(),
SymmetricalDifference(),
Union(),
VectorSplit(),
Expand Down
147 changes: 80 additions & 67 deletions python/plugins/processing/algs/qgis/SumLines.py
Expand Up @@ -28,14 +28,20 @@
import os

from qgis.PyQt.QtGui import QIcon

from qgis.core import QgsFeature, QgsFeatureSink, QgsGeometry, QgsFeatureRequest, QgsDistanceArea, QgsProcessingUtils
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsFeature,
QgsFeatureSink,
QgsField,
QgsGeometry,
QgsFeatureRequest,
QgsDistanceArea,
QgsProcessing,
QgsProcessingParameterString,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
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]

Expand All @@ -58,16 +64,16 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.LINES,
self.tr('Lines'), [dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterVector(self.POLYGONS,
self.tr('Polygons'), [dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(ParameterString(self.LEN_FIELD,
self.tr('Lines length field name', 'LENGTH')))
self.addParameter(ParameterString(self.COUNT_FIELD,
self.tr('Lines count field name', 'COUNT')))
self.addParameter(QgsProcessingParameterFeatureSource(self.LINES,
self.tr('Lines'), [QgsProcessing.TypeVectorLine]))
self.addParameter(QgsProcessingParameterFeatureSource(self.POLYGONS,
self.tr('Polygons'), [QgsProcessing.TypeVectorPolygon]))
self.addParameter(QgsProcessingParameterString(self.LEN_FIELD,
self.tr('Lines length field name'), defaultValue='LENGTH'))
self.addParameter(QgsProcessingParameterString(self.COUNT_FIELD,
self.tr('Lines count field name'), defaultValue='COUNT'))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Line length'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Line length'), QgsProcessing.TypeVectorPolygon))

def name(self):
return 'sumlinelengths'
Expand All @@ -76,66 +82,73 @@ def displayName(self):
return self.tr('Sum line lengths')

def processAlgorithm(self, parameters, context, feedback):
lineLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.LINES), context)
polyLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POLYGONS), context)
lengthFieldName = self.getParameterValue(self.LEN_FIELD)
countFieldName = self.getParameterValue(self.COUNT_FIELD)

(idxLength, fieldList) = vector.findOrCreateField(polyLayer,
polyLayer.fields(), lengthFieldName)
(idxCount, fieldList) = vector.findOrCreateField(polyLayer, fieldList,
countFieldName)

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fieldList, polyLayer.wkbType(),
polyLayer.crs(), context)

spatialIndex = QgsProcessingUtils.createSpatialIndex(lineLayer, context)

ftLine = QgsFeature()
ftPoly = QgsFeature()
outFeat = QgsFeature()
inGeom = QgsGeometry()
outGeom = QgsGeometry()
line_source = self.parameterAsSource(parameters, self.LINES, context)
poly_source = self.parameterAsSource(parameters, self.POLYGONS, context)

length_field_name = self.parameterAsString(parameters, self.LEN_FIELD, context)
count_field_name = self.parameterAsString(parameters, self.COUNT_FIELD, context)

fields = poly_source.fields()
if fields.lookupField(length_field_name) < 0:
fields.append(QgsField(length_field_name, QVariant.Double))
length_field_index = fields.lookupField(length_field_name)
if fields.lookupField(count_field_name) < 0:
fields.append(QgsField(count_field_name, QVariant.Int))
count_field_index = fields.lookupField(count_field_name)

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

spatialIndex = QgsSpatialIndex(line_source.getFeatures(
QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs())))

distArea = QgsDistanceArea()

features = QgsProcessingUtils.getFeatures(polyLayer, context)
total = 100.0 / polyLayer.featureCount() if polyLayer.featureCount() else 0
hasIntersections = False
for current, ftPoly in enumerate(features):
inGeom = ftPoly.geometry()
attrs = ftPoly.attributes()
features = poly_source.getFeatures()
total = 100.0 / poly_source.featureCount() if poly_source.featureCount() else 0
for current, poly_feature in enumerate(features):
if feedback.isCanceled():
break

output_feature = QgsFeature()
count = 0
length = 0
hasIntersections = False
lines = spatialIndex.intersects(inGeom.boundingBox())
engine = None
if len(lines) > 0:
hasIntersections = True
# use prepared geometries for faster intersection tests
engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
engine.prepareGeometry()

if hasIntersections:
request = QgsFeatureRequest().setFilterFids(lines).setSubsetOfAttributes([])
for ftLine in lineLayer.getFeatures(request):
tmpGeom = ftLine.geometry()
if engine.intersects(tmpGeom.geometry()):
outGeom = inGeom.intersection(tmpGeom)
length += distArea.measureLength(outGeom)
count += 1

outFeat.setGeometry(inGeom)
if idxLength == len(attrs):
if poly_feature.hasGeometry():
poly_geom = poly_feature.geometry()
has_intersections = False
lines = spatialIndex.intersects(poly_geom.boundingBox())
engine = None
if len(lines) > 0:
has_intersections = True
# use prepared geometries for faster intersection tests
engine = QgsGeometry.createGeometryEngine(poly_geom.geometry())
engine.prepareGeometry()

if has_intersections:
request = QgsFeatureRequest().setFilterFids(lines).setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs())
for line_feature in line_source.getFeatures(request):
if feedback.isCanceled():
break

if engine.intersects(line_feature.geometry().geometry()):
outGeom = poly_geom.intersection(line_feature.geometry())
length += distArea.measureLength(outGeom)
count += 1

output_feature.setGeometry(poly_geom)

attrs = poly_feature.attributes()
if length_field_index == len(attrs):
attrs.append(length)
else:
attrs[idxLength] = length
if idxCount == len(attrs):
attrs[length_field_index] = length
if count_field_index == len(attrs):
attrs.append(count)
else:
attrs[idxCount] = count
outFeat.setAttributes(attrs)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
attrs[count_field_index] = count
output_feature.setAttributes(attrs)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)

feedback.setProgress(int(current * total))

del writer
return {self.OUTPUT: dest_id}
32 changes: 16 additions & 16 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -1144,22 +1144,22 @@ tests:
# OUTPUT:
# name: expected/line_intersection.gml
# type: vector
#
# - algorithm: qgis:sumlinelengths
# name: Sum line lengths
# params:
# COUNT_FIELD: line_count
# LEN_FIELD: line_len
# LINES:
# name: lines.gml
# type: vector
# POLYGONS:
# name: polys.gml
# type: vector
# results:
# OUTPUT:
# name: expected/sum_line_length.gml
# type: vector

- algorithm: qgis:sumlinelengths
name: Sum line lengths
params:
COUNT_FIELD: line_count
LEN_FIELD: line_len
LINES:
name: lines.gml
type: vector
POLYGONS:
name: polys.gml
type: vector
results:
OUTPUT:
name: expected/sum_line_length.gml
type: vector

- algorithm: qgis:delaunaytriangulation
name: Delaunay triangulation (multipoint data)
Expand Down

0 comments on commit bbe466f

Please sign in to comment.