mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-08 00:05:09 -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,
|
||||
PreferClosest,
|
||||
PreferNodesNoExtraVertices,
|
||||
PreferClosestNoExtraVertices,
|
||||
EndPointPreferNodes,
|
||||
EndPointPreferClosest,
|
||||
EndPointToEndPoint,
|
||||
|
@ -61,8 +61,10 @@ class SnapGeometriesToLayer(QgisAlgorithm):
|
||||
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.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')]
|
||||
|
@ -550,6 +550,7 @@ QgsGeometry QgsGeometrySnapper::snapGeometry( const QgsGeometry &geometry, doubl
|
||||
switch ( mode )
|
||||
{
|
||||
case PreferNodes:
|
||||
case PreferNodesNoExtraVertices:
|
||||
case EndPointPreferNodes:
|
||||
case EndPointToEndPoint:
|
||||
{
|
||||
@ -568,6 +569,7 @@ QgsGeometry QgsGeometrySnapper::snapGeometry( const QgsGeometry &geometry, doubl
|
||||
}
|
||||
|
||||
case PreferClosest:
|
||||
case PreferClosestNoExtraVertices:
|
||||
case EndPointPreferClosest:
|
||||
{
|
||||
QgsPoint nodeSnap, segmentSnap;
|
||||
@ -605,7 +607,7 @@ QgsGeometry QgsGeometrySnapper::snapGeometry( const QgsGeometry &geometry, doubl
|
||||
if ( qgsgeometry_cast< const QgsPoint * >( subjGeom ) )
|
||||
return QgsGeometry( subjGeom );
|
||||
//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 );
|
||||
|
||||
// SnapIndex for subject feature
|
||||
|
@ -45,8 +45,10 @@ class ANALYSIS_EXPORT QgsGeometrySnapper : public QObject
|
||||
//! Snapping modes
|
||||
enum SnapMode
|
||||
{
|
||||
PreferNodes = 0, //!< Prefer to snap to nodes, even when a segment may be closer than a node
|
||||
PreferClosest, //!< Snap to closest point, regardless of it is a node or a segment
|
||||
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. 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
|
||||
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
|
||||
|
@ -48,6 +48,7 @@ class TestQgsGeometrySnapper : public QObject
|
||||
void endPointSnap();
|
||||
void endPointToEndPoint();
|
||||
void internalSnapper();
|
||||
void insertExtra();
|
||||
};
|
||||
|
||||
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)" ) );
|
||||
}
|
||||
|
||||
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 )
|
||||
#include "testqgsgeometrysnapper.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user