Skip to content
Permalink
Browse files

Port Line Intersection algorithm to new API

Improvements
- allow different CRS between layers
- instead of optionally allowing selection of a single field to keep from
both inputs, allow selection of multiple fields
  • Loading branch information
nyalldawson committed Jul 15, 2017
1 parent eaad18c commit 02bf88c4b7ec30409ac6d016b0bfcde23e7eab74
@@ -31,24 +31,26 @@

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

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterTableField
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 LinesIntersection(QgisAlgorithm):

INPUT_A = 'INPUT_A'
INPUT_B = 'INPUT_B'
FIELD_A = 'FIELD_A'
FIELD_B = 'FIELD_B'
INPUT = 'INPUT'
INTERSECT = 'INTERSECT'
INPUT_FIELDS = 'INPUT_FIELDS'
INTERSECT_FIELDS = 'INTERSECT_FIELDS'

OUTPUT = 'OUTPUT'

@@ -62,22 +64,23 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_A,
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterVector(self.INPUT_B,
self.tr('Intersect layer'), [dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(ParameterTableField(
self.FIELD_A,
self.tr('Input field to keep (leave as [not set] to keep all fields)'),
self.INPUT_A,
optional=True))
self.addParameter(ParameterTableField(
self.FIELD_B,
self.tr('Intersect field to keep (leave as [not set] to keep all fields)'),
self.INPUT_B,
optional=True))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Intersections'), datatype=[dataobjects.TYPE_VECTOR_POINT]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer'), [QgsProcessing.TypeVectorLine]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INTERSECT,
self.tr('Intersect layer'), [QgsProcessing.TypeVectorLine]))

self.addParameter(QgsProcessingParameterField(
self.INPUT_FIELDS,
self.tr('Input fields to keep (leave empty to keep all fields)'),
parentLayerParameterName=self.INPUT,
optional=True, allowMultiple=True))
self.addParameter(QgsProcessingParameterField(
self.INTERSECT_FIELDS,
self.tr('Intersect fields to keep (leave empty to keep all fields)'),
parentLayerParameterName=self.INTERSECT,
optional=True, allowMultiple=True))

self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Intersections'), QgsProcessing.TypeVectorPoint))

def name(self):
return 'lineintersections'
@@ -86,67 +89,82 @@ def displayName(self):
return self.tr('Line intersections')

def processAlgorithm(self, parameters, context, feedback):
layerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_A), context)
layerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_B), context)
fieldA = self.getParameterValue(self.FIELD_A)
fieldB = self.getParameterValue(self.FIELD_B)

idxA = layerA.fields().lookupField(fieldA)
idxB = layerB.fields().lookupField(fieldB)

if idxA != -1:
fieldListA = QgsFields()
fieldListA.append(layerA.fields()[idxA])
sourceA = self.parameterAsSource(parameters, self.INPUT, context)
sourceB = self.parameterAsSource(parameters, self.INTERSECT, context)

fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS, context)
fieldsB = self.parameterAsFields(parameters, self.INTERSECT_FIELDS, context)

fieldListA = QgsFields()
field_indices_a = []
if len(fieldsA) > 0:
for f in fieldsA:
idxA = sourceA.fields().lookupField(f)
if idxA >= 0:
field_indices_a.append(idxA)
fieldListA.append(sourceA.fields()[idxA])
else:
fieldListA = layerA.fields()

if idxB != -1:
fieldListB = QgsFields()
fieldListB.append(layerB.fields()[idxB])
fieldListA = sourceA.fields()
field_indices_a = [i for i in range(0, fieldListA.count())]

fieldListB = QgsFields()
field_indices_b = []
if len(fieldsB) > 0:
for f in fieldsB:
idxB = sourceB.fields().lookupField(f)
if idxB >= 0:
field_indices_b.append(idxB)
fieldListB.append(sourceB.fields()[idxB])
else:
fieldListB = layerB.fields()
fieldListB = sourceB.fields()
field_indices_b = [i for i in range(0, fieldListB.count())]

fieldListB = vector.testForUniqueness(fieldListA, fieldListB)
for b in fieldListB:
fieldListA.append(b)

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fieldListA, QgsWkbTypes.Point, layerA.crs(),
context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fieldListA, QgsWkbTypes.Point, sourceA.sourceCrs())

spatialIndex = QgsProcessingUtils.createSpatialIndex(layerB, context)
spatialIndex = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback)

outFeat = QgsFeature()
features = QgsProcessingUtils.getFeatures(layerA, context)
total = 100.0 / layerA.featureCount() if layerA.featureCount() else 0
hasIntersections = False

features = sourceA.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(field_indices_a))
total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 0
for current, inFeatA in enumerate(features):
if feedback.isCanceled():
break

if not inFeatA.hasGeometry():
continue

inGeom = inFeatA.geometry()
hasIntersections = False
has_intersections = False
lines = spatialIndex.intersects(inGeom.boundingBox())

engine = None
if len(lines) > 0:
hasIntersections = True
has_intersections = True
# use prepared geometries for faster intersection tests
engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
engine.prepareGeometry()

if hasIntersections:
if has_intersections:
request = QgsFeatureRequest().setFilterFids(lines)
for inFeatB in layerB.getFeatures(request):
request.setDestinationCrs(sourceA.sourceCrs())
request.setSubsetOfAttributes(field_indices_b)

for inFeatB in sourceB.getFeatures(request):
if feedback.isCanceled():
break

tmpGeom = inFeatB.geometry()

points = []
attrsA = inFeatA.attributes()
if idxA != -1:
attrsA = [attrsA[idxA]]
attrsB = inFeatB.attributes()
if idxB != -1:
attrsB = [attrsB[idxB]]

if engine.intersects(tmpGeom.geometry()):
tempGeom = inGeom.intersection(tmpGeom)
out_attributes = [inFeatA.attributes()[i] for i in field_indices_a]
out_attributes.extend([inFeatB.attributes()[i] for i in field_indices_b])
if tempGeom.type() == QgsWkbTypes.PointGeometry:
if tempGeom.isMultipart():
points = tempGeom.asMultiPoint()
@@ -155,10 +173,9 @@ def processAlgorithm(self, parameters, context, feedback):

for j in points:
outFeat.setGeometry(tempGeom.fromPoint(j))
attrsA.extend(attrsB)
outFeat.setAttributes(attrsA)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
outFeat.setAttributes(out_attributes)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

feedback.setProgress(int(current * total))

del writer
return {self.OUTPUT: dest_id}
@@ -64,6 +64,7 @@
from .ImportIntoPostGIS import ImportIntoPostGIS
from .ImportIntoSpatialite import ImportIntoSpatialite
from .Intersection import Intersection
from .LinesIntersection import LinesIntersection
from .LinesToPolygons import LinesToPolygons
from .Merge import Merge
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
@@ -91,7 +92,6 @@
from .ZonalStatistics import ZonalStatistics

# from .ExtractByLocation import ExtractByLocation
# from .LinesIntersection import LinesIntersection
# from .MeanCoords import MeanCoords
# from .PointDistance import PointDistance
# from .UniqueValues import UniqueValues
@@ -184,7 +184,7 @@ def __init__(self):

def getAlgs(self):
# algs = [MeanCoords(),
# LinesIntersection(), UniqueValues(), PointDistance(),
# UniqueValues(), PointDistance(),
# ExportGeometryInfo(),
# SinglePartsToMultiparts(),
# ExtractNodes(),
@@ -258,6 +258,7 @@ def getAlgs(self):
ImportIntoPostGIS(),
ImportIntoSpatialite(),
Intersection(),
LinesIntersection(),
LinesToPolygons(),
Merge(),
NearestNeighbourAnalysis(),
@@ -1112,38 +1112,38 @@ tests:
# OUTPUT_LAYER:
# hash: 7fe0e0174185fd743e23760f33615adf10f771b4275f320db6f7f4f8
# type: rasterhash
#
# # Case 1: Keep all fields
# - algorithm: qgis:lineintersections
# name: Line Intersection Keep All Fields from Both
# params:
# INPUT_A:
# name: lines.gml
# type: vector
# INPUT_B:
# name: simplify_lines.gml
# type: vector
# results:
# OUTPUT:
# name: expected/line_intersection.gml
# type: vector
#
# # Case 2: Keep fid field from both layers
# - algorithm: qgis:lineintersections
# name: Line Intersection Keep fid from Both
# params:
# FIELD_A: fid
# FIELD_B: fid
# INPUT_A:
# name: lines.gml
# type: vector
# INPUT_B:
# name: simplify_lines.gml
# type: vector
# results:
# OUTPUT:
# name: expected/line_intersection.gml
# type: vector

# Case 1: Keep all fields
- algorithm: qgis:lineintersections
name: Line Intersection Keep All Fields from Both
params:
INPUT:
name: lines.gml
type: vector
INTERSECT:
name: simplify_lines.gml
type: vector
results:
OUTPUT:
name: expected/line_intersection.gml
type: vector

# Case 2: Keep fid field from both layers
- algorithm: qgis:lineintersections
name: Line Intersection Keep fid from Both
params:
INPUT_FIELDS: fid
INTERSECT_FIELDS: fid
INPUT:
name: lines.gml
type: vector
INTERSECT:
name: simplify_lines.gml
type: vector
results:
OUTPUT:
name: expected/line_intersection.gml
type: vector

- algorithm: qgis:sumlinelengths
name: Sum line lengths

0 comments on commit 02bf88c

Please sign in to comment.
You can’t perform that action at this time.