mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-08 00:02:35 -05:00
when snapping line layers Because the default behavior of the snapper is to insert extra vertices into the snapped geometry in order to make it 'follow' the reference geometries exactly, this can result in unwanted results for line layers where the resultant snapped layer has overlapping line segments. Since we can't always know what the desired result is that the user wants (maybe they do want overlapping lines), instead give them control over the result by exposing extra enum options which never insert extra vertices.
126 lines
5.5 KiB
Python
126 lines
5.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
***************************************************************************
|
|
SnapGeometries.py
|
|
-----------------
|
|
Date : October 2016
|
|
Copyright : (C) 2016 by Nyall Dawson
|
|
Email : nyall dot dawson at gmail dot com
|
|
***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************
|
|
"""
|
|
|
|
__author__ = 'Nyall Dawson'
|
|
__date__ = 'October 2016'
|
|
__copyright__ = '(C) 2016, Nyall Dawson'
|
|
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
|
|
|
__revision__ = '$Format:%H$'
|
|
|
|
from qgis.analysis import (QgsGeometrySnapper,
|
|
QgsInternalGeometrySnapper)
|
|
from qgis.core import (QgsFeatureSink,
|
|
QgsProcessing,
|
|
QgsProcessingParameterFeatureSource,
|
|
QgsProcessingParameterFeatureSink,
|
|
QgsProcessingParameterNumber,
|
|
QgsProcessingParameterEnum)
|
|
|
|
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
|
|
|
|
|
|
class SnapGeometriesToLayer(QgisAlgorithm):
|
|
|
|
INPUT = 'INPUT'
|
|
REFERENCE_LAYER = 'REFERENCE_LAYER'
|
|
TOLERANCE = 'TOLERANCE'
|
|
OUTPUT = 'OUTPUT'
|
|
BEHAVIOR = 'BEHAVIOR'
|
|
|
|
def group(self):
|
|
return self.tr('Vector geometry')
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def initAlgorithm(self, config=None):
|
|
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'), [QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorLine, QgsProcessing.TypeVectorPolygon]))
|
|
self.addParameter(QgsProcessingParameterFeatureSource(self.REFERENCE_LAYER, self.tr('Reference layer'),
|
|
[QgsProcessing.TypeVectorPoint,
|
|
QgsProcessing.TypeVectorLine,
|
|
QgsProcessing.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, insert extra vertices where required'),
|
|
self.tr('Prefer closest point, insert extra vertices where required'),
|
|
self.tr('Prefer aligning nodes, don\'t insert new vertices'),
|
|
self.tr('Prefer closest point, don\'t insert new vertices'),
|
|
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(QgsProcessingParameterEnum(
|
|
self.BEHAVIOR,
|
|
self.tr('Behavior'),
|
|
options=self.modes, defaultValue=0))
|
|
|
|
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Snapped geometry')))
|
|
|
|
def name(self):
|
|
return 'snapgeometries'
|
|
|
|
def displayName(self):
|
|
return self.tr('Snap geometries to layer')
|
|
|
|
def processAlgorithm(self, parameters, context, feedback):
|
|
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))
|
|
sink.addFeature(out_feature, QgsFeatureSink.FastInsert)
|
|
processed += 1
|
|
feedback.setProgress(processed * total)
|
|
|
|
return {self.OUTPUT: dest_id}
|