Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't add unnecessary points on intersection when tracing #59055

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions python/PyQt6/core/auto_generated/qgstracer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ The optional "error" argument may receive error code (PathError enum) if it is n
Find out whether the point is snapped to a vertex or edge (i.e. it can be used for tracing start/stop)
%End

void setAddPointsOnIntersectionsEnabled( bool enable );
%Docstring
When ``enable`` is ``True``, the shortest path's straight segments will include vertices where the input layers intersect, even if
no such vertex existed on the input layers

.. versionadded:: 3.40
%End

bool addPointsOnIntersectionsEnabled() const;
%Docstring
Returns whether the shortest path's straight segments will include vertices where the input layers intersect, even if
no such vertex existed on the input layers

.. versionadded:: 3.40
%End
protected:

virtual void configure();
Expand Down
15 changes: 15 additions & 0 deletions python/core/auto_generated/qgstracer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ The optional "error" argument may receive error code (PathError enum) if it is n
Find out whether the point is snapped to a vertex or edge (i.e. it can be used for tracing start/stop)
%End

void setAddPointsOnIntersectionsEnabled( bool enable );
%Docstring
When ``enable`` is ``True``, the shortest path's straight segments will include vertices where the input layers intersect, even if
no such vertex existed on the input layers

.. versionadded:: 3.40
%End

bool addPointsOnIntersectionsEnabled() const;
%Docstring
Returns whether the shortest path's straight segments will include vertices where the input layers intersect, even if
no such vertex existed on the input layers

.. versionadded:: 3.40
%End
protected:

virtual void configure();
Expand Down
49 changes: 49 additions & 0 deletions src/core/qgstracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,19 @@ bool QgsTracer::initGraph()
// GEOSNode_r may throw an exception
geos::unique_ptr allGeomGeos( QgsGeos::asGeos( allGeom ) );
geos::unique_ptr allNoded( GEOSNode_r( QgsGeosContext::get(), allGeomGeos.get() ) );

if ( mAddPointsOnIntersections )
{
mIntersections = QgsGeometry();
}
else
{
geos::unique_ptr allPoints( GEOSGeom_extractUniquePoints_r( QgsGeosContext::get(), allGeomGeos.get() ) );
geos::unique_ptr nodedPoints( GEOSGeom_extractUniquePoints_r( QgsGeosContext::get(), allNoded.get() ) );
geos::unique_ptr intersectionNodes( GEOSDifference_r( QgsGeosContext::get(), nodedPoints.get(), allPoints.get() ) );
mIntersections = QgsGeos::geometryFromGeos( intersectionNodes.release() );
}

timeNodingCall = t2a.elapsed();

QgsGeometry noded = QgsGeos::geometryFromGeos( allNoded.release() );
Expand Down Expand Up @@ -768,6 +781,33 @@ QVector<QgsPointXY> QgsTracer::findShortestPath( const QgsPointXY &p1, const Qgs
Q_UNUSED( tPath )
QgsDebugMsgLevel( QStringLiteral( "path timing: prep %1 ms, path %2 ms" ).arg( tPrep ).arg( tPath ), 2 );

if ( points.size() > 2 && !mIntersections.isEmpty() )
{
QVector<QgsPointXY> noInts;
noInts.reserve( points.size() );
noInts.append( points.first() );
for ( auto it = std::next( points.begin() ), end = std::prev( points.end() ); it != end; ++it )
{
if ( mIntersections.contains( it->x(), it->y() ) )
{
// we skip points that are on a straight segment and were not on the original geometries
QgsPointXY nearest;
if ( 0 == it->sqrDistToSegment( std::prev( it )->x(),
std::prev( it )->y(),
std::next( it )->x(),
std::next( it )->y(),
nearest, 1E-12 ) )
{
continue;
}
}
noInts << *it;
}
noInts.append( points.last() );
points = noInts;
QgsDebugMsgLevel( QStringLiteral( "intersection point removal timing: %1 ms" ).arg( t2.elapsed() - tPath ), 2 );
}

resetGraph( *mGraph );

if ( !points.isEmpty() && mOffset != 0 )
Expand Down Expand Up @@ -813,3 +853,12 @@ bool QgsTracer::isPointSnapped( const QgsPointXY &pt )
int e = point2edge( *mGraph, pt, lineVertexAfter );
return e != -1;
}

void QgsTracer::setAddPointsOnIntersectionsEnabled( bool enable )
{
if ( enable == mAddPointsOnIntersections )
return;

mAddPointsOnIntersections = enable;
invalidateGraph();
}
17 changes: 17 additions & 0 deletions src/core/qgstracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,19 @@ class CORE_EXPORT QgsTracer : public QObject
//! Find out whether the point is snapped to a vertex or edge (i.e. it can be used for tracing start/stop)
bool isPointSnapped( const QgsPointXY &pt );

/**
* When \a enable is TRUE, the shortest path's straight segments will include vertices where the input layers intersect, even if
* no such vertex existed on the input layers
* \since QGIS 3.40
*/
void setAddPointsOnIntersectionsEnabled( bool enable );

/**
* Returns whether the shortest path's straight segments will include vertices where the input layers intersect, even if
* no such vertex existed on the input layers
* \since QGIS 3.40
*/
bool addPointsOnIntersectionsEnabled() const { return mAddPointsOnIntersections; }
protected:

/**
Expand Down Expand Up @@ -185,6 +198,10 @@ class CORE_EXPORT QgsTracer : public QObject
std::unique_ptr<QgsRenderContext> mRenderContext;
//! Extent for graph building (empty extent means no limit)
QgsRectangle mExtent;
//! If FALSE, no vertices will be added on intersections unless they exist in the original layers
bool mAddPointsOnIntersections = false;
//! Holds the input layers' intersections. Only populated when mAddPointsOnIntersections == false
QgsGeometry mIntersections;

//! Offset in map units that should be applied to the traced paths
double mOffset = 0;
Expand Down
45 changes: 45 additions & 0 deletions tests/src/core/testqgstracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class TestQgsTracer : public QObject
void testSimple();
void testPolygon();
void testButterfly();
void testAddPointsOnIntersections();
void testLayerUpdates();
void testExtent();
void testReprojection();
Expand Down Expand Up @@ -306,6 +307,50 @@ void TestQgsTracer::testButterfly()
delete vl;
}

void TestQgsTracer::testAddPointsOnIntersections()
{
// checks whether tracer adds vertices at intersections

QStringList wkts;
wkts << QStringLiteral( "LINESTRING(0 0, 10 10)" )
<< QStringLiteral( "LINESTRING(0 10, 10 0)" );

/* This shape (without a vertex where the linestring crosses itself):
* + + 10,10
* \/
* /\
* + +
* 0,0
*/

QgsVectorLayer *vl = make_layer( wkts );

QgsTracer tracer;
tracer.setLayers( QList<QgsVectorLayer *>() << vl );

QgsPolylineXY points = tracer.findShortestPath( QgsPointXY( 0, 0 ), QgsPointXY( 10, 10 ) );

QCOMPARE( points.count(), 2 );
QCOMPARE( points[0], QgsPointXY( 0, 0 ) );
QCOMPARE( points[1], QgsPointXY( 10, 10 ) );

// now enable adding points on intersections
tracer.setAddPointsOnIntersectionsEnabled( true );
points = tracer.findShortestPath( QgsPointXY( 0, 0 ), QgsPointXY( 10, 10 ) );
QCOMPARE( points.count(), 3 );
QCOMPARE( points[0], QgsPointXY( 0, 0 ) );
QCOMPARE( points[1], QgsPointXY( 5, 5 ) );
QCOMPARE( points[2], QgsPointXY( 10, 10 ) );

// and disable it again
tracer.setAddPointsOnIntersectionsEnabled( false );
points = tracer.findShortestPath( QgsPointXY( 0, 0 ), QgsPointXY( 10, 10 ) );
QCOMPARE( points.count(), 2 );
QCOMPARE( points[0], QgsPointXY( 0, 0 ) );
QCOMPARE( points[1], QgsPointXY( 10, 10 ) );
delete vl;
}

void TestQgsTracer::testLayerUpdates()
{
// check whether the tracer is updated on added/removed/changed features
Expand Down
Loading