Skip to content
Permalink
Browse files

Port snap geometries algorithm to new API

  • Loading branch information
nyalldawson committed Jul 6, 2017
1 parent 05364aa commit 90f10ae8538e789b877e3172c45528ded96c09ef
@@ -69,6 +69,7 @@
from .SelectByExpression import SelectByExpression
from .SimplifyGeometries import SimplifyGeometries
from .Smooth import Smooth
from .SnapGeometries import SnapGeometriesToLayer
from .SpatialiteExecuteSQL import SpatialiteExecuteSQL
from .SymmetricalDifference import SymmetricalDifference
from .VectorSplit import VectorSplit
@@ -156,7 +157,6 @@
# from .ExtendLines import ExtendLines
# from .ExtractSpecificNodes import ExtractSpecificNodes
# from .GeometryByExpression import GeometryByExpression
# from .SnapGeometries import SnapGeometriesToLayer
# from .PoleOfInaccessibility import PoleOfInaccessibility
# from .RasterCalculator import RasterCalculator
# from .Heatmap import Heatmap
@@ -230,7 +230,7 @@ def getAlgs(self):
# IdwInterpolation(), TinInterpolation(),
# RemoveNullGeometry(),
# ExtendLines(), ExtractSpecificNodes(),
# GeometryByExpression(), SnapGeometriesToLayer(),
# GeometryByExpression(),
# PoleOfInaccessibility(),
#
# RasterCalculator(), Heatmap(), Orthogonalize(),
@@ -269,6 +269,7 @@ def getAlgs(self):
SelectByExpression(),
SimplifyGeometries(),
Smooth(),
SnapGeometriesToLayer(),
SpatialiteExecuteSQL(),
SymmetricalDifference(),
VectorSplit(),
@@ -27,14 +27,15 @@

from qgis.analysis import (QgsGeometrySnapper,
QgsInternalGeometrySnapper)
from qgis.core import (QgsApplication,
QgsFeature,
QgsFeatureSink,
QgsProcessingUtils)
from qgis.core import (QgsFeatureSink,
QgsProcessingParameterDefinition,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterNumber,
QgsProcessingParameterEnum,
QgsProcessingOutputVectorLayer)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector, ParameterNumber, ParameterSelection
from processing.core.outputs import OutputVector


class SnapGeometriesToLayer(QgisAlgorithm):
@@ -50,20 +51,28 @@ def group(self):

def __init__(self):
super().__init__()
self.addParameter(ParameterVector(self.INPUT, self.tr('Input layer')))
self.addParameter(ParameterVector(self.REFERENCE_LAYER, self.tr('Reference layer')))
self.addParameter(ParameterNumber(self.TOLERANCE, self.tr('Tolerance (layer units)'), 0.00000001, 9999999999, default=10.0))

self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'), [QgsProcessingParameterDefinition.TypeVectorPoint, QgsProcessingParameterDefinition.TypeVectorLine, QgsProcessingParameterDefinition.TypeVectorPolygon]))
self.addParameter(QgsProcessingParameterFeatureSource(self.REFERENCE_LAYER, self.tr('Reference layer'),
[QgsProcessingParameterDefinition.TypeVectorPoint,
QgsProcessingParameterDefinition.TypeVectorLine,
QgsProcessingParameterDefinition.TypeVectorPolygon]))

self.addParameter(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Tolerance (layer units)'), type=QgsProcessingParameterNumber.Double,
minValue=0.00000001, maxValue=9999999999, defaultValue=10.0))

self.modes = [self.tr('Prefer aligning nodes'),
self.tr('Prefer closest point'),
self.tr('Move end points only, prefer aligning nodes'),
self.tr('Move end points only, prefer closest point'),
self.tr('Snap end points to end points only')]
self.addParameter(ParameterSelection(
self.addParameter(QgsProcessingParameterEnum(
self.BEHAVIOR,
self.tr('Behavior'),
self.modes, default=0))
self.addOutput(OutputVector(self.OUTPUT, self.tr('Snapped geometries')))
options=self.modes, defaultValue=0))

self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Snapped geometry')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Snapped geometry")))

def name(self):
return 'snapgeometries'
@@ -72,39 +81,44 @@ def displayName(self):
return self.tr('Snap geometries to layer')

def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context)
reference_layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.REFERENCE_LAYER), context)
tolerance = self.getParameterValue(self.TOLERANCE)
mode = self.getParameterValue(self.BEHAVIOR)

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(),
context)

features = QgsProcessingUtils.getFeatures(layer, context)

self.processed = 0
self.feedback = feedback
self.total = 100.0 / layer.featureCount() if layer.featureCount() else 0

if self.getParameterValue(self.INPUT) != self.getParameterValue(self.REFERENCE_LAYER):
snapper = QgsGeometrySnapper(reference_layer)
snapper.featureSnapped.connect(self.featureSnapped)
snapped_features = snapper.snapFeatures(features, tolerance, mode)
for f in snapped_features:
writer.addFeature(f, QgsFeatureSink.FastInsert)
source = self.parameterAsSource(parameters, self.INPUT, context)

reference_source = self.parameterAsSource(parameters, self.REFERENCE_LAYER, context)
tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context)
mode = self.parameterAsEnum(parameters, self.BEHAVIOR, context)

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

features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0

if parameters[self.INPUT] != parameters[self.REFERENCE_LAYER]:
snapper = QgsGeometrySnapper(reference_source)
processed = 0
for f in features:
if feedback.isCanceled():
break
if f.hasGeometry():
out_feature = f
out_feature.setGeometry(snapper.snapGeometry(f.geometry(), tolerance, mode))
sink.addFeature(out_feature, QgsFeatureSink.FastInsert)
else:
sink.addFeature(f)
processed += 1
feedback.setProgress(processed * total)
else:
# snapping internally
snapper = QgsInternalGeometrySnapper(tolerance, mode)
processed = 0
for f in features:
if feedback.isCanceled():
break

out_feature = f
out_feature.setGeometry(snapper.snapFeature(f))
writer.addFeature(out_feature, QgsFeatureSink.FastInsert)
sink.addFeature(out_feature, QgsFeatureSink.FastInsert)
processed += 1
feedback.setProgress(processed * self.total)

del writer
feedback.setProgress(processed * total)

def featureSnapped(self):
self.processed += 1
self.feedback.setProgress(int(self.processed * self.total))
return {self.OUTPUT: dest_id}
@@ -1376,100 +1376,100 @@ tests:
# OUTPUT_LAYER:
# name: expected/geometry_by_expression_line.gml
# type: vector
#
# - algorithm: qgis:snapgeometries
# name: Snap lines to lines
# params:
# INPUT:
# name: snap_lines.gml
# type: vector
# REFERENCE_LAYER:
# name: lines.gml
# type: vector
# TOLERANCE: 1.0
# results:
# OUTPUT:
# name: expected/snap_lines_to_lines.gml
# type: vector
#
# - algorithm: qgis:snapgeometries
# name: Snap polygons to polygons
# params:
# INPUT:
# name: snap_polys.gml
# type: vector
# REFERENCE_LAYER:
# name: polys.gml
# type: vector
# TOLERANCE: 1.0
# results:
# OUTPUT:
# name: expected/snap_polys_to_polys.gml
# type: vector
#
# - algorithm: qgis:snapgeometries
# name: Snap points to points
# params:
# INPUT:
# name: snap_points.gml
# type: vector
# REFERENCE_LAYER:
# name: points.gml
# type: vector
# TOLERANCE: 1.0
# results:
# OUTPUT:
# name: expected/snap_points_to_points.gml
# type: vector
#
# - algorithm: qgis:snapgeometries
# name: Snap points to lines (prefer nodes)
# params:
# BEHAVIOR: '0'
# INPUT:
# name: snap_points.gml
# type: vector
# REFERENCE_LAYER:
# name: lines.gml
# type: vector
# TOLERANCE: 1.0
# results:
# OUTPUT:
# name: expected/snap_point_to_lines_prefer_nodes.gml
# type: vector
#
# - algorithm: qgis:snapgeometries
# name: Snap points to lines (prefer closest)
# params:
# BEHAVIOR: '1'
# INPUT:
# name: snap_points.gml
# type: vector
# REFERENCE_LAYER:
# name: lines.gml
# type: vector
# TOLERANCE: 1.0
# results:
# OUTPUT:
# name: expected/snap_point_to_lines_prefer_closest.gml
# type: vector
#
# - algorithm: qgis:snapgeometries
# name: Snap internal
# params:
# BEHAVIOR: '0'
# INPUT:
# name: custom/snap_internal.gml
# type: vector
# REFERENCE_LAYER:
# name: custom/snap_internal.gml
# type: vector
# TOLERANCE: 1.0
# results:
# OUTPUT:
# name: expected/snap_internal.gml
# type: vector
#

- algorithm: qgis:snapgeometries
name: Snap lines to lines
params:
INPUT:
name: snap_lines.gml
type: vector
REFERENCE_LAYER:
name: lines.gml
type: vector
TOLERANCE: 1.0
results:
OUTPUT:
name: expected/snap_lines_to_lines.gml
type: vector

- algorithm: qgis:snapgeometries
name: Snap polygons to polygons
params:
INPUT:
name: snap_polys.gml
type: vector
REFERENCE_LAYER:
name: polys.gml
type: vector
TOLERANCE: 1.0
results:
OUTPUT:
name: expected/snap_polys_to_polys.gml
type: vector

- algorithm: qgis:snapgeometries
name: Snap points to points
params:
INPUT:
name: snap_points.gml
type: vector
REFERENCE_LAYER:
name: points.gml
type: vector
TOLERANCE: 1.0
results:
OUTPUT:
name: expected/snap_points_to_points.gml
type: vector

- algorithm: qgis:snapgeometries
name: Snap points to lines (prefer nodes)
params:
BEHAVIOR: '0'
INPUT:
name: snap_points.gml
type: vector
REFERENCE_LAYER:
name: lines.gml
type: vector
TOLERANCE: 1.0
results:
OUTPUT:
name: expected/snap_point_to_lines_prefer_nodes.gml
type: vector

- algorithm: qgis:snapgeometries
name: Snap points to lines (prefer closest)
params:
BEHAVIOR: '1'
INPUT:
name: snap_points.gml
type: vector
REFERENCE_LAYER:
name: lines.gml
type: vector
TOLERANCE: 1.0
results:
OUTPUT:
name: expected/snap_point_to_lines_prefer_closest.gml
type: vector

- algorithm: qgis:snapgeometries
name: Snap internal
params:
BEHAVIOR: '0'
INPUT:
name: custom/snap_internal.gml
type: vector
REFERENCE_LAYER:
name: custom/snap_internal.gml
type: vector
TOLERANCE: 1.0
results:
OUTPUT:
name: expected/snap_internal.gml
type: vector

# - algorithm: qgis:poleofinaccessibility
# name: Pole of inaccessibility (polygons)
# params:

0 comments on commit 90f10ae

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