From 384da5665ecd4bc4f42d986d39532de16818286a Mon Sep 17 00:00:00 2001 From: Daan Goedkoop Date: Wed, 27 Jan 2016 23:39:17 +0100 Subject: [PATCH] Fix floating point inaccuracies when digitizing (to fix bug #13745) --- python/gui/qgsmapmouseevent.sip | 7 +++++ python/gui/qgsmaptoolcapture.sip | 12 +++++++++ src/app/qgsmaptooladdfeature.cpp | 14 ++++++++-- src/app/qgsmaptooladdpart.cpp | 2 +- src/app/qgsmaptooladdring.cpp | 2 +- src/app/qgsmaptoolreshape.cpp | 2 +- src/app/qgsmaptoolsplitfeatures.cpp | 2 +- src/gui/qgsmapmouseevent.h | 7 +++++ src/gui/qgsmaptoolcapture.cpp | 42 +++++++++++++++++++++++++++-- src/gui/qgsmaptoolcapture.h | 13 +++++++++ 10 files changed, 95 insertions(+), 8 deletions(-) diff --git a/python/gui/qgsmapmouseevent.sip b/python/gui/qgsmapmouseevent.sip index f72efd23625..e8859e217c5 100644 --- a/python/gui/qgsmapmouseevent.sip +++ b/python/gui/qgsmapmouseevent.sip @@ -78,6 +78,13 @@ class QgsMapMouseEvent : QMouseEvent */ QgsPoint mapPoint() const; + /** + * Returns the matching data from the most recently snapped point. + * @return the snapping data structure + * @note added in 2.14 + */ + QgsPointLocator::Match mapPointMatch() const; + /** * Set the (snapped) point this event points to in map coordinates. * The point in pixel coordinates will be calculated accordingly. diff --git a/python/gui/qgsmaptoolcapture.sip b/python/gui/qgsmaptoolcapture.sip index e37285e3756..6e4a9257a60 100644 --- a/python/gui/qgsmaptoolcapture.sip +++ b/python/gui/qgsmaptoolcapture.sip @@ -112,10 +112,22 @@ class QgsMapToolCapture : public QgsMapToolAdvancedDigitizing */ int nextPoint( QPoint p, QgsPointV2 &layerPoint, QgsPointV2 &mapPoint ); + /** Fetches the original point from the source layer if it has the same + * CRS as the current layer. + * @return 0 in case of success, 1 if not applicable (CRS mismatch), 2 in case of failure + * @note added in 2.14 */ + int fetchLayerPoint( QgsPointLocator::Match match, QgsPointV2& layerPoint ); + /** Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates) @return 0 in case of success, 1 if current layer is not a vector layer, 2 if coordinate transformation failed*/ int addVertex( const QgsPoint& point ); + /** Variant to supply more information in the case of snapping + @param mapPoint The vertex to add in map coordinates + @param match Data about the snapping match. Can be an invalid match, if point not snapped. + @note added in 2.14 */ + int addVertex( const QgsPoint& mapPoint, QgsPointLocator::Match match ); + /** Removes the last vertex from mRubberBand and mCaptureList*/ void undo(); diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index ee00ba7f344..51e336f4276 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -113,7 +113,17 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e ) QgsPoint savePoint; //point in layer coordinates try { - savePoint = toLayerCoordinates( vlayer, e->mapPoint() ); + QgsPointV2 fetchPoint; + int res; + res = fetchLayerPoint( e->mapPointMatch(), fetchPoint ); + if ( res == 0 ) + { + savePoint = QgsPoint( fetchPoint.x(), fetchPoint.y() ); + } + else + { + savePoint = toLayerCoordinates( vlayer, e->mapPoint() ); + } QgsDebugMsg( "savePoint = " + savePoint.toString() ); } catch ( QgsCsException &cse ) @@ -184,7 +194,7 @@ void QgsMapToolAddFeature::cadCanvasReleaseEvent( QgsMapMouseEvent* e ) //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { - int error = addVertex( e->mapPoint() ); + int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer diff --git a/src/app/qgsmaptooladdpart.cpp b/src/app/qgsmaptooladdpart.cpp index 95d943ff8a8..72d0b1f6804 100644 --- a/src/app/qgsmaptooladdpart.cpp +++ b/src/app/qgsmaptooladdpart.cpp @@ -95,7 +95,7 @@ void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { - int error = addVertex( e->mapPoint() ); + int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { QgsDebugMsg( "current layer is not a vector layer" ); diff --git a/src/app/qgsmaptooladdring.cpp b/src/app/qgsmaptooladdring.cpp index e28cd175096..20d294458d0 100644 --- a/src/app/qgsmaptooladdring.cpp +++ b/src/app/qgsmaptooladdring.cpp @@ -58,7 +58,7 @@ void QgsMapToolAddRing::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { - int error = addVertex( e->mapPoint() ); + int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer diff --git a/src/app/qgsmaptoolreshape.cpp b/src/app/qgsmaptoolreshape.cpp index 3ba58068613..414c3fcc92b 100644 --- a/src/app/qgsmaptoolreshape.cpp +++ b/src/app/qgsmaptoolreshape.cpp @@ -50,7 +50,7 @@ void QgsMapToolReshape::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { - int error = addVertex( e->mapPoint() ); + int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer diff --git a/src/app/qgsmaptoolsplitfeatures.cpp b/src/app/qgsmaptoolsplitfeatures.cpp index 2edef768a5a..041afe936d0 100644 --- a/src/app/qgsmaptoolsplitfeatures.cpp +++ b/src/app/qgsmaptoolsplitfeatures.cpp @@ -67,7 +67,7 @@ void QgsMapToolSplitFeatures::cadCanvasReleaseEvent( QgsMapMouseEvent * e ) } } - int error = addVertex( e->mapPoint() ); + int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { //current layer is not a vector layer diff --git a/src/gui/qgsmapmouseevent.h b/src/gui/qgsmapmouseevent.h index 151fd2ffe97..05b4df9157d 100644 --- a/src/gui/qgsmapmouseevent.h +++ b/src/gui/qgsmapmouseevent.h @@ -93,6 +93,13 @@ class GUI_EXPORT QgsMapMouseEvent : public QMouseEvent */ inline QgsPoint mapPoint() const { return mMapPoint; } + /** + * Returns the matching data from the most recently snapped point. + * @return the snapping data structure + * @note added in 2.14 + */ + QgsPointLocator::Match mapPointMatch() const { return mSnapMatch; } + /** * Set the (snapped) point this event points to in map coordinates. * The point in pixel coordinates will be calculated accordingly. diff --git a/src/gui/qgsmaptoolcapture.cpp b/src/gui/qgsmaptoolcapture.cpp index 8aa76642fbf..36f1d0e6443 100644 --- a/src/gui/qgsmaptoolcapture.cpp +++ b/src/gui/qgsmaptoolcapture.cpp @@ -367,7 +367,41 @@ int QgsMapToolCapture::nextPoint( QPoint p, QgsPoint &layerPoint, QgsPoint &mapP Q_NOWARN_DEPRECATED_POP } +int QgsMapToolCapture::fetchLayerPoint( QgsPointLocator::Match match , QgsPointV2 &layerPoint ) +{ + QgsVectorLayer* vlayer = qobject_cast( mCanvas->currentLayer() ); + QgsVectorLayer* sourceLayer = match.layer(); + if ( match.isValid() && match.hasVertex() && sourceLayer && + ( sourceLayer->crs() == vlayer->crs() ) ) + { + QgsFeature f; + QgsFeatureRequest request; + QgsPoint foundPoint; + request.setFilterFid( match.featureId() ); + bool fetched = match.layer()->getFeatures( request ).nextFeature( f ); + if ( fetched ) + { + foundPoint = f.geometry()->vertexAt( match.vertexIndex() ); + layerPoint = QgsPointV2( foundPoint.x(), foundPoint.y() ); + return 0; + } + else + { + return 2; + } + } + else + { + return 1; + } +} + int QgsMapToolCapture::addVertex( const QgsPoint& point ) +{ + return addVertex( point, QgsPointLocator::Match() ); +} + +int QgsMapToolCapture::addVertex( const QgsPoint& point, QgsPointLocator::Match match ) { if ( mode() == CaptureNone ) { @@ -377,10 +411,14 @@ int QgsMapToolCapture::addVertex( const QgsPoint& point ) int res; QgsPointV2 layerPoint; - res = nextPoint( QgsPointV2( point ), layerPoint ); + res = fetchLayerPoint( match, layerPoint ); if ( res != 0 ) { - return res; + res = nextPoint( QgsPointV2( point ), layerPoint ); + if ( res != 0 ) + { + return res; + } } if ( !mRubberBand ) diff --git a/src/gui/qgsmaptoolcapture.h b/src/gui/qgsmaptoolcapture.h index facedb3b099..ef87074aa2f 100644 --- a/src/gui/qgsmaptoolcapture.h +++ b/src/gui/qgsmaptoolcapture.h @@ -22,6 +22,7 @@ #include "qgspoint.h" #include "qgsgeometry.h" #include "qgslayertreeview.h" +#include "qgspointlocator.h" #include #include @@ -132,10 +133,22 @@ class GUI_EXPORT QgsMapToolCapture : public QgsMapToolAdvancedDigitizing */ int nextPoint( QPoint p, QgsPointV2 &layerPoint, QgsPointV2 &mapPoint ); + /** Fetches the original point from the source layer if it has the same + * CRS as the current layer. + * @return 0 in case of success, 1 if not applicable (CRS mismatch), 2 in case of failure + * @note added in 2.14 */ + int fetchLayerPoint( QgsPointLocator::Match match, QgsPointV2& layerPoint ); + /** Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates) @return 0 in case of success, 1 if current layer is not a vector layer, 2 if coordinate transformation failed*/ int addVertex( const QgsPoint& point ); + /** Variant to supply more information in the case of snapping + @param mapPoint The vertex to add in map coordinates + @param match Data about the snapping match. Can be an invalid match, if point not snapped. + @note added in 2.14 */ + int addVertex( const QgsPoint& mapPoint, QgsPointLocator::Match match ); + /** Removes the last vertex from mRubberBand and mCaptureList*/ void undo();