Skip to content
Permalink
Browse files

Merge pull request #4862 from nyalldawson/nn

Port Line Intersection algorithm to new API
  • Loading branch information
nyalldawson committed Jul 15, 2017
2 parents eaad18c + a6736ce commit 8333f6a59deb9880214e4ecdbfcf5783389d6699
@@ -31,14 +31,15 @@

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

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.tools import vector
@@ -60,6 +61,8 @@ class Intersection(QgisAlgorithm):
INPUT = 'INPUT'
OVERLAY = 'OVERLAY'
OUTPUT = 'OUTPUT'
INPUT_FIELDS = 'INPUT_FIELDS'
OVERLAY_FIELDS = 'OVERLAY_FIELDS'

def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'intersect.png'))
@@ -76,6 +79,17 @@ def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterFeatureSource(self.OVERLAY,
self.tr('Intersection layer')))

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.OVERLAY_FIELDS,
self.tr('Intersect fields to keep (leave empty to keep all fields)'),
parentLayerParameterName=self.OVERLAY,
optional=True, allowMultiple=True))

self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Intersection')))

def name(self):
@@ -89,27 +103,61 @@ def processAlgorithm(self, parameters, context, feedback):
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)

geomType = QgsWkbTypes.multiType(sourceA.wkbType())
fields = vector.combineFields(sourceA.fields(), sourceB.fields())

fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS, context)
fieldsB = self.parameterAsFields(parameters, self.OVERLAY_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 = 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 = 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)

(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, geomType, sourceA.sourceCrs())
fieldListA, geomType, sourceA.sourceCrs())

outFeat = QgsFeature()
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback)

total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 1
count = 0

for featA in sourceA.getFeatures():
for featA in sourceA.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(field_indices_a)):
if feedback.isCanceled():
break

if not featA.hasGeometry():
continue

geom = featA.geometry()
atMapA = featA.attributes()
intersects = indexB.intersects(geom.boundingBox())

request = QgsFeatureRequest().setFilterFids(intersects)
request.setDestinationCrs(sourceA.sourceCrs())
request.setSubsetOfAttributes(field_indices_b)

engine = None
if len(intersects) > 0:
@@ -123,7 +171,8 @@ def processAlgorithm(self, parameters, context, feedback):

tmpGeom = featB.geometry()
if engine.intersects(tmpGeom.geometry()):
atMapB = featB.attributes()
out_attributes = [featA.attributes()[i] for i in field_indices_a]
out_attributes.extend([featB.attributes()[i] for i in field_indices_b])
int_geom = QgsGeometry(geom.intersection(tmpGeom))
if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection:
int_com = geom.combine(tmpGeom)
@@ -139,10 +188,7 @@ def processAlgorithm(self, parameters, context, feedback):
try:
if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[int_geom.wkbType()]]:
outFeat.setGeometry(int_geom)
attrs = []
attrs.extend(atMapA)
attrs.extend(atMapB)
outFeat.setAttributes(attrs)
outFeat.setAttributes(out_attributes)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
raise QgsProcessingException(
@@ -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(),
Binary file not shown.

0 comments on commit 8333f6a

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