From 2ab862990f74a4b33c25ec7e81a09c2322ffeeab Mon Sep 17 00:00:00 2001 From: Yoann Quenach de Quivillic Date: Tue, 17 Dec 2024 15:43:06 +0100 Subject: [PATCH] Display the limit as an infinite line --- src/app/qgsmaptooltrimextendfeature.cpp | 75 ++++++++++++++++++++++++- src/app/qgsmaptooltrimextendfeature.h | 8 ++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/app/qgsmaptooltrimextendfeature.cpp b/src/app/qgsmaptooltrimextendfeature.cpp index a064677c5ea0..90f8db012f1d 100644 --- a/src/app/qgsmaptooltrimextendfeature.cpp +++ b/src/app/qgsmaptooltrimextendfeature.cpp @@ -48,6 +48,7 @@ QgsMapToolTrimExtendFeature::QgsMapToolTrimExtendFeature( QgsMapCanvas *canvas ) : QgsMapToolEdit( canvas ) { mToolName = tr( "Trim/Extend feature" ); + connect( mCanvas, &QgsMapCanvas::extentsChanged, this, &QgsMapToolTrimExtendFeature::extendLimit ); } static bool getPoints( const QgsPointLocator::Match &match, QgsPoint &p1, QgsPoint &p2 ) @@ -89,15 +90,19 @@ void QgsMapToolTrimExtendFeature::canvasMoveEvent( QgsMapMouseEvent *e ) QgsPointXY p1, p2; match.edgePoints( p1, p2 ); - + mLimitLayer = match.layer(); mRubberBandLimit.reset( createRubberBand( Qgis::GeometryType::Line ) ); + mRubberBandLimitExtend.reset( createRubberBand( Qgis::GeometryType::Line ) ); + mRubberBandLimitExtend->setLineStyle( Qt::DotLine ); mRubberBandLimit->addPoint( p1 ); mRubberBandLimit->addPoint( p2 ); mRubberBandLimit->show(); + extendLimit(); } else if ( mRubberBandLimit ) { mRubberBandLimit->hide(); + mRubberBandLimitExtend.reset(); } break; case StepExtend: @@ -198,8 +203,10 @@ void QgsMapToolTrimExtendFeature::canvasMoveEvent( QgsMapMouseEvent *e ) if ( mIsModified ) { mGeom.removeDuplicateNodes(); - mRubberBandExtend->setToGeometry( mGeom, mVlayer ); + // Densify by count to better display the intersection when layer crs != map crs + mRubberBandExtend->setToGeometry( mGeom.densifyByCount( 10 ), mVlayer ); mRubberBandExtend->show(); + extendLimit(); } } else @@ -294,6 +301,69 @@ void QgsMapToolTrimExtendFeature::keyPressEvent( QKeyEvent *e ) } } + +void QgsMapToolTrimExtendFeature::extendLimit() +{ + if ( !mRubberBandLimitExtend ) + { + return; + } + + QgsVectorLayer *refLayer = qobject_cast( mCanvas->currentLayer() ); + refLayer = refLayer ? refLayer : mVlayer ? mVlayer + : mLimitLayer; + + // Compute intersection between the line that extends the limit segment and the + // edges of the map canvas + QgsPointXY p1 = toLayerCoordinates( refLayer, *mRubberBandLimit->getPoint( 0, 0 ) ); + QgsPointXY p2 = toLayerCoordinates( refLayer, *mRubberBandLimit->getPoint( 0, 1 ) ); + QgsPoint canvasTopLeft = QgsPoint( toLayerCoordinates( refLayer, QPoint( 0, 0 ) ) ); + QgsPoint canvasTopRight = QgsPoint( toLayerCoordinates( refLayer, QPoint( mCanvas->width(), 0 ) ) ); + QgsPoint canvasBottomLeft = QgsPoint( toLayerCoordinates( refLayer, QPoint( 0, mCanvas->height() ) ) ); + QgsPoint canvasBottomRight = QgsPoint( toLayerCoordinates( refLayer, QPoint( mCanvas->width(), mCanvas->height() ) ) ); + + QList points; + points << p1 << p2; + + QgsPoint intersection; + if ( QgsGeometryUtils::lineIntersection( QgsPoint( p1 ), QgsPoint( p2 ) - QgsPoint( p1 ), canvasTopLeft, canvasTopRight - canvasTopLeft, intersection ) ) + { + points << QgsPointXY( intersection ); + } + if ( QgsGeometryUtils::lineIntersection( QgsPoint( p1 ), QgsPoint( p2 ) - QgsPoint( p1 ), canvasTopRight, canvasBottomRight - canvasTopRight, intersection ) ) + { + points << QgsPointXY( intersection ); + } + if ( QgsGeometryUtils::lineIntersection( QgsPoint( p1 ), QgsPoint( p2 ) - QgsPoint( p1 ), canvasBottomRight, canvasBottomLeft - canvasBottomRight, intersection ) ) + { + points << QgsPointXY( intersection ); + } + if ( QgsGeometryUtils::lineIntersection( QgsPoint( p1 ), QgsPoint( p2 ) - QgsPoint( p1 ), canvasBottomLeft, canvasTopLeft - canvasBottomLeft, intersection ) ) + { + points << QgsPointXY( intersection ); + } + + // Reorder the points by x/y coordinates + std::sort( points.begin(), points.end(), []( const QgsPointXY &a, const QgsPointXY &b ) -> bool { + if ( a.x() == b.x() ) + return a.y() < b.y(); + return a.x() < b.x(); + } ); + + // Keep only the closest intersection points from the original points + const int p1Idx = points.indexOf( p1 ); + const int p2Idx = points.indexOf( p2 ); + const int first = std::max( 0, std::min( p1Idx, p2Idx ) - 1 ); + const int last = std::min( points.size() - 1, std::max( p1Idx, p2Idx ) + 1 ); + const QgsPolylineXY polyline = points.mid( first, last - first + 1 ).toVector(); + + // Densify the polyline to display a more accurate prediction when layer crs != canvas crs + QgsGeometry geom = QgsGeometry::fromPolylineXY( polyline ).densifyByCount( 10 ); + + mRubberBandLimitExtend->setToGeometry( geom, refLayer ); + mRubberBandLimitExtend->show(); +} + void QgsMapToolTrimExtendFeature::deactivate() { mStep = StepLimit; @@ -302,6 +372,7 @@ void QgsMapToolTrimExtendFeature::deactivate() mIsIntersection = false; mSegmentIntersects = false; mRubberBandLimit.reset(); + mRubberBandLimitExtend.reset(); mRubberBandExtend.reset(); mRubberBandIntersection.reset(); QgsMapTool::deactivate(); diff --git a/src/app/qgsmaptooltrimextendfeature.h b/src/app/qgsmaptooltrimextendfeature.h index 20283a13d028..9e46b64d15bd 100644 --- a/src/app/qgsmaptooltrimextendfeature.h +++ b/src/app/qgsmaptooltrimextendfeature.h @@ -38,9 +38,15 @@ class APP_EXPORT QgsMapToolTrimExtendFeature : public QgsMapToolEdit //! called when map tool is being deactivated void deactivate() override; + private slots: + // Recompute the extended limit + void extendLimit(); + private: - //! Rubberband that shows the limit + //! Rubberband that highlights the limit segment std::unique_ptr mRubberBandLimit; + //! Rubberband that extends the limit segment + std::unique_ptr mRubberBandLimitExtend; //! Rubberband that shows the feature being extended std::unique_ptr mRubberBandExtend; //! Rubberband that shows the intersection point