mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Merge pull request #4862 from nyalldawson/nn
Port Line Intersection algorithm to new API
This commit is contained in:
commit
8333f6a59d
@ -31,6 +31,7 @@ from qgis.PyQt.QtGui import QIcon
|
||||
|
||||
from qgis.core import (QgsFeatureRequest,
|
||||
QgsFeature,
|
||||
QgsFields,
|
||||
QgsFeatureSink,
|
||||
QgsGeometry,
|
||||
QgsWkbTypes,
|
||||
@ -38,7 +39,7 @@ from qgis.core import (QgsFeatureRequest,
|
||||
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 @@ class Intersection(QgisAlgorithm):
|
||||
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,10 +103,40 @@ class Intersection(QgisAlgorithm):
|
||||
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)
|
||||
@ -100,16 +144,20 @@ class Intersection(QgisAlgorithm):
|
||||
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 @@ class Intersection(QgisAlgorithm):
|
||||
|
||||
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 @@ class Intersection(QgisAlgorithm):
|
||||
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,13 +31,15 @@ from qgis.PyQt.QtGui import QIcon
|
||||
|
||||
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]
|
||||
@ -45,10 +47,10 @@ 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 @@ class LinesIntersection(QgisAlgorithm):
|
||||
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.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
|
||||
self.tr('Input layer'), [QgsProcessing.TypeVectorLine]))
|
||||
self.addParameter(QgsProcessingParameterFeatureSource(self.INTERSECT,
|
||||
self.tr('Intersect layer'), [QgsProcessing.TypeVectorLine]))
|
||||
|
||||
self.addOutput(OutputVector(self.OUTPUT, self.tr('Intersections'), datatype=[dataobjects.TYPE_VECTOR_POINT]))
|
||||
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 @@ class LinesIntersection(QgisAlgorithm):
|
||||
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)
|
||||
sourceA = self.parameterAsSource(parameters, self.INPUT, context)
|
||||
sourceB = self.parameterAsSource(parameters, self.INTERSECT, context)
|
||||
|
||||
idxA = layerA.fields().lookupField(fieldA)
|
||||
idxB = layerB.fields().lookupField(fieldB)
|
||||
fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS, context)
|
||||
fieldsB = self.parameterAsFields(parameters, self.INTERSECT_FIELDS, context)
|
||||
|
||||
if idxA != -1:
|
||||
fieldListA = QgsFields()
|
||||
fieldListA.append(layerA.fields()[idxA])
|
||||
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()
|
||||
fieldListA = sourceA.fields()
|
||||
field_indices_a = [i for i in range(0, fieldListA.count())]
|
||||
|
||||
if idxB != -1:
|
||||
fieldListB = QgsFields()
|
||||
fieldListB.append(layerB.fields()[idxB])
|
||||
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 @@ class LinesIntersection(QgisAlgorithm):
|
||||
|
||||
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 .Hillshade import Hillshade
|
||||
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 .VoronoiPolygons import VoronoiPolygons
|
||||
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 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
||||
|
||||
def getAlgs(self):
|
||||
# algs = [MeanCoords(),
|
||||
# LinesIntersection(), UniqueValues(), PointDistance(),
|
||||
# UniqueValues(), PointDistance(),
|
||||
# ExportGeometryInfo(),
|
||||
# SinglePartsToMultiparts(),
|
||||
# ExtractNodes(),
|
||||
@ -258,6 +258,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
||||
ImportIntoPostGIS(),
|
||||
ImportIntoSpatialite(),
|
||||
Intersection(),
|
||||
LinesIntersection(),
|
||||
LinesToPolygons(),
|
||||
Merge(),
|
||||
NearestNeighbourAnalysis(),
|
||||
|
Binary file not shown.
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user