mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-09 00:08:52 -04:00
Fix geometry snapper sometimes creates unwanted overlapping segments
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.
This commit is contained in:
parent
d0e927a84f
commit
928afdd8c5
@ -28,6 +28,8 @@ class QgsGeometrySnapper : QObject
|
|||||||
{
|
{
|
||||||
PreferNodes,
|
PreferNodes,
|
||||||
PreferClosest,
|
PreferClosest,
|
||||||
|
PreferNodesNoExtraVertices,
|
||||||
|
PreferClosestNoExtraVertices,
|
||||||
EndPointPreferNodes,
|
EndPointPreferNodes,
|
||||||
EndPointPreferClosest,
|
EndPointPreferClosest,
|
||||||
EndPointToEndPoint,
|
EndPointToEndPoint,
|
||||||
|
@ -61,8 +61,10 @@ class SnapGeometriesToLayer(QgisAlgorithm):
|
|||||||
self.addParameter(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Tolerance (layer units)'), type=QgsProcessingParameterNumber.Double,
|
self.addParameter(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Tolerance (layer units)'), type=QgsProcessingParameterNumber.Double,
|
||||||
minValue=0.00000001, maxValue=9999999999, defaultValue=10.0))
|
minValue=0.00000001, maxValue=9999999999, defaultValue=10.0))
|
||||||
|
|
||||||
self.modes = [self.tr('Prefer aligning nodes'),
|
self.modes = [self.tr('Prefer aligning nodes, insert extra vertices where required'),
|
||||||
self.tr('Prefer closest point'),
|
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 aligning nodes'),
|
||||||
self.tr('Move end points only, prefer closest point'),
|
self.tr('Move end points only, prefer closest point'),
|
||||||
self.tr('Snap end points to end points only')]
|
self.tr('Snap end points to end points only')]
|
||||||
|
@ -550,6 +550,7 @@ QgsGeometry QgsGeometrySnapper::snapGeometry( const QgsGeometry &geometry, doubl
|
|||||||
switch ( mode )
|
switch ( mode )
|
||||||
{
|
{
|
||||||
case PreferNodes:
|
case PreferNodes:
|
||||||
|
case PreferNodesNoExtraVertices:
|
||||||
case EndPointPreferNodes:
|
case EndPointPreferNodes:
|
||||||
case EndPointToEndPoint:
|
case EndPointToEndPoint:
|
||||||
{
|
{
|
||||||
@ -568,6 +569,7 @@ QgsGeometry QgsGeometrySnapper::snapGeometry( const QgsGeometry &geometry, doubl
|
|||||||
}
|
}
|
||||||
|
|
||||||
case PreferClosest:
|
case PreferClosest:
|
||||||
|
case PreferClosestNoExtraVertices:
|
||||||
case EndPointPreferClosest:
|
case EndPointPreferClosest:
|
||||||
{
|
{
|
||||||
QgsPoint nodeSnap, segmentSnap;
|
QgsPoint nodeSnap, segmentSnap;
|
||||||
@ -605,7 +607,7 @@ QgsGeometry QgsGeometrySnapper::snapGeometry( const QgsGeometry &geometry, doubl
|
|||||||
if ( qgsgeometry_cast< const QgsPoint * >( subjGeom ) )
|
if ( qgsgeometry_cast< const QgsPoint * >( subjGeom ) )
|
||||||
return QgsGeometry( subjGeom );
|
return QgsGeometry( subjGeom );
|
||||||
//or for end point snapping
|
//or for end point snapping
|
||||||
if ( mode == EndPointPreferClosest || mode == EndPointPreferNodes || mode == EndPointToEndPoint )
|
if ( mode == PreferClosestNoExtraVertices || mode == PreferNodesNoExtraVertices || mode == EndPointPreferClosest || mode == EndPointPreferNodes || mode == EndPointToEndPoint )
|
||||||
return QgsGeometry( subjGeom );
|
return QgsGeometry( subjGeom );
|
||||||
|
|
||||||
// SnapIndex for subject feature
|
// SnapIndex for subject feature
|
||||||
|
@ -45,8 +45,10 @@ class ANALYSIS_EXPORT QgsGeometrySnapper : public QObject
|
|||||||
//! Snapping modes
|
//! Snapping modes
|
||||||
enum SnapMode
|
enum SnapMode
|
||||||
{
|
{
|
||||||
PreferNodes = 0, //!< Prefer to snap to nodes, even when a segment may be closer than a node
|
PreferNodes = 0, //!< Prefer to snap to nodes, even when a segment may be closer than a node. New nodes will be inserted to make geometries follow each other exactly when inside allowable tolerance.
|
||||||
PreferClosest, //!< Snap to closest point, regardless of it is a node or a segment
|
PreferClosest, //!< Snap to closest point, regardless of it is a node or a segment. New nodes will be inserted to make geometries follow each other exactly when inside allowable tolerance.
|
||||||
|
PreferNodesNoExtraVertices, //!< Prefer to snap to nodes, even when a segment may be closer than a node. No new nodes will be inserted.
|
||||||
|
PreferClosestNoExtraVertices, //!< Snap to closest point, regardless of it is a node or a segment. No new nodes will be inserted.
|
||||||
EndPointPreferNodes, //!< Only snap start/end points of lines (point features will also be snapped, polygon features will not be modified), prefer to snap to nodes
|
EndPointPreferNodes, //!< Only snap start/end points of lines (point features will also be snapped, polygon features will not be modified), prefer to snap to nodes
|
||||||
EndPointPreferClosest, //!< Only snap start/end points of lines (point features will also be snapped, polygon features will not be modified), snap to closest point
|
EndPointPreferClosest, //!< Only snap start/end points of lines (point features will also be snapped, polygon features will not be modified), snap to closest point
|
||||||
EndPointToEndPoint, //!< Only snap the start/end points of lines to other start/end points of lines
|
EndPointToEndPoint, //!< Only snap the start/end points of lines to other start/end points of lines
|
||||||
|
@ -48,6 +48,7 @@ class TestQgsGeometrySnapper : public QObject
|
|||||||
void endPointSnap();
|
void endPointSnap();
|
||||||
void endPointToEndPoint();
|
void endPointToEndPoint();
|
||||||
void internalSnapper();
|
void internalSnapper();
|
||||||
|
void insertExtra();
|
||||||
};
|
};
|
||||||
|
|
||||||
void TestQgsGeometrySnapper::initTestCase()
|
void TestQgsGeometrySnapper::initTestCase()
|
||||||
@ -499,6 +500,90 @@ void TestQgsGeometrySnapper::internalSnapper()
|
|||||||
QCOMPARE( res.value( 4 ).asWkt(), QStringLiteral( "LineString (0 0, 5 5, 10 10, 15 15)" ) );
|
QCOMPARE( res.value( 4 ).asWkt(), QStringLiteral( "LineString (0 0, 5 5, 10 10, 15 15)" ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsGeometrySnapper::insertExtra()
|
||||||
|
{
|
||||||
|
// test extra node insertion behaviour
|
||||||
|
QgsGeometry refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(0 0, 0.1 0, 0.2 0, 9.8 0, 9.9 0, 10 0, 10.1 0, 10.2 0, 20 0)" ) );
|
||||||
|
QgsFeature f1( 1 );
|
||||||
|
f1.setGeometry( refGeom );
|
||||||
|
|
||||||
|
// inserting extra nodes
|
||||||
|
QgsInternalGeometrySnapper snapper( 2, QgsGeometrySnapper::PreferNodes );
|
||||||
|
QgsGeometry result = snapper.snapFeature( f1 );
|
||||||
|
QCOMPARE( result.asWkt(), f1.geometry().asWkt() );
|
||||||
|
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(8 -5, 9 0, 10 5)" ) );
|
||||||
|
QgsFeature f2( 2 );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (8 -5, 9.8 0, 9.9 0, 10 0, 10.1 0, 10 5)" ) );
|
||||||
|
|
||||||
|
// reset snapper
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::PreferNodes );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(7 -2, 10 0)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
// should 'follow' line for a bit
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (7 -2, 9.8 0, 9.9 0, 10 0)" ) );
|
||||||
|
|
||||||
|
// using PreferNodesNoExtraVertices mode, no extra vertices should be inserted
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::PreferNodesNoExtraVertices );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(8 -5, 9 0.1, 10 5)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (8 -5, 9.8 0, 10 5)" ) );
|
||||||
|
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::PreferNodesNoExtraVertices );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(7 -2, 10.1 0.1)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (7 -2, 10.1 0)" ) );
|
||||||
|
|
||||||
|
// using PreferClosestNoExtraVertices mode, no extra vertices should be inserted
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::PreferClosestNoExtraVertices );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(8 -5, 9 0.1, 10 5)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (8 -5, 9 0, 10 5)" ) );
|
||||||
|
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::PreferClosestNoExtraVertices );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(7 -2, 10.1 0.1)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (7 -2, 10.1 0)" ) );
|
||||||
|
|
||||||
|
// using EndPointPreferNodes mode, no extra vertices should be inserted
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::EndPointPreferNodes );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(7 -2, 10.02 0)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (7 -2, 10 0)" ) );
|
||||||
|
|
||||||
|
// using EndPointPreferClosest mode, no extra vertices should be inserted
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::EndPointPreferClosest );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(7 -2, 10.02 0)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (7 -2, 10 0)" ) );
|
||||||
|
|
||||||
|
// using EndPointToEndPoint mode, no extra vertices should be inserted
|
||||||
|
snapper = QgsInternalGeometrySnapper( 2, QgsGeometrySnapper::EndPointToEndPoint );
|
||||||
|
result = snapper.snapFeature( f1 );
|
||||||
|
refGeom = QgsGeometry::fromWkt( QStringLiteral( "LineString(-7 -2, 0.12 0)" ) );
|
||||||
|
f2.setGeometry( refGeom );
|
||||||
|
result = snapper.snapFeature( f2 );
|
||||||
|
QCOMPARE( result.asWkt( 1 ), QStringLiteral( "LineString (-7 -2, 0 0)" ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QGSTEST_MAIN( TestQgsGeometrySnapper )
|
QGSTEST_MAIN( TestQgsGeometrySnapper )
|
||||||
#include "testqgsgeometrysnapper.moc"
|
#include "testqgsgeometrysnapper.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user