From b870b556836c5a74fbc5d960e5f1f11d9fe20966 Mon Sep 17 00:00:00 2001 From: "Juergen E. Fischer" Date: Tue, 29 May 2012 22:01:12 +0200 Subject: [PATCH] node tool fixes: - fix OTFR support (fixes #5327.16) - smaller snap tolerance for geographic CRSs (fixes #5661) - use different snapping epsilons in QgsVectorLayer::snapToGeometry() for layers with geographic and projected crs. - [API] enhancements (to fix #5661): * QgsGeometry::closestSegmentWithContext: allow passing of segment snapping epsilon * QgsPoint::sqrDistToSegment: allow assing of segment snapping epsilon * QgsMapLayer::crs() add "const" --- src/app/nodetool/qgsmaptoolnodetool.cpp | 79 +++++++++++-------------- src/app/nodetool/qgsmaptoolnodetool.h | 9 +-- src/core/qgis.h | 3 + src/core/qgsgeometry.cpp | 11 ++-- src/core/qgsgeometry.h | 3 +- src/core/qgsmaplayer.cpp | 2 +- src/core/qgsmaplayer.h | 2 +- src/core/qgspoint.cpp | 4 +- src/core/qgspoint.h | 4 +- src/core/qgsvectorlayer.cpp | 3 +- src/gui/qgsprojectionselector.h | 3 +- 11 files changed, 57 insertions(+), 66 deletions(-) diff --git a/src/app/nodetool/qgsmaptoolnodetool.cpp b/src/app/nodetool/qgsmaptoolnodetool.cpp index 1774bb0da126..41eb8b19196d 100644 --- a/src/app/nodetool/qgsmaptoolnodetool.cpp +++ b/src/app/nodetool/qgsmaptoolnodetool.cpp @@ -249,6 +249,7 @@ void QgsMapToolNodeTool::canvasMoveEvent( QMouseEvent * e ) } } createMovingRubberBands(); + QList snapResults; QgsPoint posMapCoord = snapPointFromResults( snapResults, e->pos() ); mPosMapCoordBackup = posMapCoord; @@ -257,26 +258,29 @@ void QgsMapToolNodeTool::canvasMoveEvent( QMouseEvent * e ) { // move rubberband QList snapResults; - QgsPoint firstCoords = toMapCoordinates( mPressCoordinates ); - QList excludePoints; - excludePoints.append( mClosestVertex ); - mSnapper.snapToBackgroundLayers( e->pos(), snapResults, excludePoints ); + mSnapper.snapToBackgroundLayers( e->pos(), snapResults, QList() << mClosestMapVertex ); // get correct coordinates to move to QgsPoint posMapCoord = snapPointFromResults( snapResults, e->pos() ); + + QgsPoint pressMapCoords; if ( snapResults.size() > 0 ) { - firstCoords = toMapCoordinates( vlayer, mClosestVertex ); + pressMapCoords = mClosestMapVertex; + } + else + { + pressMapCoords = toMapCoordinates( mPressCoordinates ); } + QgsVector offset = posMapCoord - pressMapCoords; + // handle points if ( mIsPoint ) { - double offsetX = posMapCoord.x() - firstCoords.x(); - double offsetY = posMapCoord.y() - firstCoords.y(); for ( int i = 0; i < mRubberBands.size(); i++ ) { - mRubberBands[i]->setTranslationOffset( offsetX, offsetY ); + mRubberBands[i]->setTranslationOffset( offset.x(), offset.y() ); } return; } @@ -285,25 +289,21 @@ void QgsMapToolNodeTool::canvasMoveEvent( QMouseEvent * e ) QList &vertexMap = mSelectedFeature->vertexMap(); for ( int i = 0; i < vertexMap.size(); i++ ) { + if ( !vertexMap[i]->isSelected() ) + continue; - if ( vertexMap[i]->isSelected() ) - { - QgsPoint mapCoords = toMapCoordinates( vlayer, vertexMap[i]->point() ); - double x = mapCoords.x() + posMapCoord.x() - firstCoords.x(); - double y = mapCoords.y() + posMapCoord.y() - firstCoords.y(); + QgsPoint p = toMapCoordinates( vlayer, vertexMap[i]->point() ) + offset; - mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( vertexMap[i]->rubberBandIndex(), QgsPoint( x, y ) ); + mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( vertexMap[i]->rubberBandIndex(), p ); - if ( vertexMap[i]->rubberBandIndex() == 0 ) - { - mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( 0, QgsPoint( x, y ) ); - } + if ( vertexMap[i]->rubberBandIndex() == 0 ) + { + mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( 0, p ); } } // topological editing - double offsetX = posMapCoord.x() - mPosMapCoordBackup.x(); - double offsetY = posMapCoord.y() - mPosMapCoordBackup.y(); + offset = posMapCoord - mPosMapCoordBackup; for ( int i = 0; i < mTopologyRubberBand.size(); i++ ) { for ( int pointIndex = 0; pointIndex < mTopologyRubberBand[i]->numberOfVertices() - 1; pointIndex++ ) @@ -315,10 +315,10 @@ void QgsMapToolNodeTool::canvasMoveEvent( QMouseEvent * e ) { break; } - mTopologyRubberBand[i]->movePoint( pointIndex, QgsPoint( point->x() + offsetX, point->y() + offsetY ) ); + mTopologyRubberBand[i]->movePoint( pointIndex, *point + offset ); if ( pointIndex == 0 ) { - mTopologyRubberBand[i]->movePoint( pointIndex , QgsPoint( point->x() + offsetX, point->y() + offsetY ) ); + mTopologyRubberBand[i]->movePoint( pointIndex, *point + offset ); } } } @@ -370,13 +370,14 @@ void QgsMapToolNodeTool::canvasPressEvent( QMouseEvent * e ) else { // some feature already selected - QgsPoint mapCoordPoint = toMapCoordinates( e->pos() ); + QgsPoint layerCoordPoint = toLayerCoordinates( vlayer, e->pos() ); + double tol = QgsTolerance::vertexSearchRadius( vlayer, mCanvas->mapRenderer() ); // get geometry and find if snapping is near it int atVertex, beforeVertex, afterVertex; double dist; - mSelectedFeature->geometry()->closestVertex( toLayerCoordinates( vlayer, mapCoordPoint ), atVertex, beforeVertex, afterVertex, dist ); + QgsPoint closestLayerVertex = mSelectedFeature->geometry()->closestVertex( layerCoordPoint, atVertex, beforeVertex, afterVertex, dist ); dist = sqrt( dist ); mSnapper.snapToCurrentLayer( e->pos(), snapResults, QgsSnapper::SnapToVertex, tol ); @@ -384,8 +385,7 @@ void QgsMapToolNodeTool::canvasPressEvent( QMouseEvent * e ) { // some vertex selected mMoving = true; - QgsPoint point = toMapCoordinates( e->pos() ); - mClosestVertex = closestVertex( toLayerCoordinates( vlayer, point ) ); + mClosestMapVertex = toMapCoordinates( vlayer, closestLayerVertex ); if ( mMoving ) { if ( mSelectedFeature->isSelected( atVertex ) ) @@ -437,8 +437,7 @@ void QgsMapToolNodeTool::canvasPressEvent( QMouseEvent * e ) if ( !mSelectAnother ) { mMoving = true; - QgsPoint point = toMapCoordinates( e->pos() ); - mClosestVertex = closestVertex( toLayerCoordinates( vlayer, point ) ); + mClosestMapVertex = toMapCoordinates( vlayer, closestLayerVertex ); if ( mIsPoint ) { @@ -519,14 +518,14 @@ void QgsMapToolNodeTool::canvasReleaseEvent( QMouseEvent * e ) mMoving = false; QList snapResults; - mSnapper.snapToBackgroundLayers( e->pos(), snapResults, QList() << mClosestVertex ); + mSnapper.snapToBackgroundLayers( e->pos(), snapResults, QList() << mClosestMapVertex ); - QgsPoint releaseCoords = snapPointFromResults( snapResults, e->pos() ); - QgsPoint pressCoords; + QgsPoint releaseLayerCoords = toLayerCoordinates( vlayer, snapPointFromResults( snapResults, e->pos() ) ); + QgsPoint pressLayerCoords; if ( snapResults.size() > 0 ) { - pressCoords = toLayerCoordinates( vlayer, mClosestVertex ); + pressLayerCoords = toLayerCoordinates( vlayer, mClosestMapVertex ); int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); if ( topologicalEditing ) @@ -536,10 +535,10 @@ void QgsMapToolNodeTool::canvasReleaseEvent( QMouseEvent * e ) } else { - pressCoords = toLayerCoordinates( vlayer, mPressCoordinates ); + pressLayerCoords = toLayerCoordinates( vlayer, mPressCoordinates ); } - mSelectedFeature->moveSelectedVertexes( releaseCoords - pressCoords ); + mSelectedFeature->moveSelectedVertexes( releaseLayerCoords - pressLayerCoords ); mCanvas->refresh(); } else // selecting vertexes by rubberband @@ -645,8 +644,7 @@ void QgsMapToolNodeTool::canvasDoubleClickEvent( QMouseEvent * e ) return; // some segment selected - QgsPoint coords = snapResults.first().snappedVertex; - QgsPoint layerCoords = toLayerCoordinates( vlayer, coords ); + QgsPoint layerCoords = toLayerCoordinates( vlayer, snapResults.first().snappedVertex ); if ( topologicalEditing ) { // snap from adding position to this vertex when topological editing is enabled @@ -677,15 +675,6 @@ void QgsMapToolNodeTool::canvasDoubleClickEvent( QMouseEvent * e ) mCanvas->refresh(); } -QgsPoint QgsMapToolNodeTool::closestVertex( QgsPoint point ) -{ - int at; - int before; - int after; - double dist; - return mSelectedFeature->geometry()->closestVertex( point, at, before, after, dist ); -} - void QgsMapToolNodeTool::keyPressEvent( QKeyEvent* e ) { if ( e->key() == Qt::Key_Control ) diff --git a/src/app/nodetool/qgsmaptoolnodetool.h b/src/app/nodetool/qgsmaptoolnodetool.h index 110546f3bb01..142b0cc1baa3 100644 --- a/src/app/nodetool/qgsmaptoolnodetool.h +++ b/src/app/nodetool/qgsmaptoolnodetool.h @@ -52,11 +52,6 @@ class QgsMapToolNodeTool: public QgsMapToolVertexEdit //! called when map tool is being deactivated void deactivate(); - /** - * Returns closest vertex to given point from selected feature - */ - QgsPoint closestVertex( QgsPoint point ); - public slots: void selectedFeatureDestroyed(); @@ -134,8 +129,8 @@ class QgsMapToolNodeTool: public QgsMapToolVertexEdit /** stored position of last press down action to count how much vertexes should be moved */ QPoint mPressCoordinates; - /** closest vertex to click */ - QgsPoint mClosestVertex; + /** closest vertex to click in map coordinates */ + QgsPoint mClosestMapVertex; /** backup of map coordinates to be able to count change between moves */ QgsPoint mPosMapCoordBackup; diff --git a/src/core/qgis.h b/src/core/qgis.h index 0b9381d0cbb6..156b2d46cd3e 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -186,6 +186,9 @@ const double MINIMUM_POINT_SIZE = 0.1; const double DEFAULT_POINT_SIZE = 2.0; const double DEFAULT_LINE_WIDTH = 0.26; +/** default snapping tolerance for segments (@note added in 1.8) */ +const double DEFAULT_SEGMENT_EPSILON = 1e-8; + // FIXME: also in qgisinterface.h #ifndef QGISEXTERN #ifdef WIN32 diff --git a/src/core/qgsgeometry.cpp b/src/core/qgsgeometry.cpp index 0a213e4f993c..aef7e45e969e 100644 --- a/src/core/qgsgeometry.cpp +++ b/src/core/qgsgeometry.cpp @@ -2409,7 +2409,8 @@ double QgsGeometry::closestSegmentWithContext( const QgsPoint& point, QgsPoint& minDistPoint, int& afterVertex, - double* leftOf ) + double* leftOf, + double epsilon ) { QgsDebugMsg( "Entering." ); QgsPoint distPoint; @@ -2470,7 +2471,7 @@ double QgsGeometry::closestSegmentWithContext( if ( index > 0 ) { - if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint ) ) < sqrDist ) + if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint, epsilon ) ) < sqrDist ) { closestSegmentIndex = index; sqrDist = testdist; @@ -2518,7 +2519,7 @@ double QgsGeometry::closestSegmentWithContext( } if ( prevx && prevy ) { - if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint ) ) < sqrDist ) + if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint, epsilon ) ) < sqrDist ) { closestSegmentIndex = pointindex; sqrDist = testdist; @@ -2564,7 +2565,7 @@ double QgsGeometry::closestSegmentWithContext( } if ( prevx && prevy ) { - if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint ) ) < sqrDist ) + if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint, epsilon ) ) < sqrDist ) { closestSegmentIndex = index; sqrDist = testdist; @@ -2616,7 +2617,7 @@ double QgsGeometry::closestSegmentWithContext( } if ( prevx && prevy ) { - if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint ) ) < sqrDist ) + if (( testdist = point.sqrDistToSegment( *prevx, *prevy, *thisx, *thisy, distPoint, epsilon ) ) < sqrDist ) { closestSegmentIndex = pointindex; sqrDist = testdist; diff --git a/src/core/qgsgeometry.h b/src/core/qgsgeometry.h index 43679d6c4774..78ac89a0e5c5 100644 --- a/src/core/qgsgeometry.h +++ b/src/core/qgsgeometry.h @@ -242,9 +242,10 @@ class CORE_EXPORT QgsGeometry * @param afterVertex Receives index of the vertex after the closest segment. The vertex * before the closest segment is always afterVertex - 1 * @param leftOf Out: Returns if the point lies on the left of right side of the segment ( < 0 means left, > 0 means right ) + * @param epsilon epsilon for segment snapping (added in 1.8) * @return The squared cartesian distance is also returned in sqrDist, negative number on error */ - double closestSegmentWithContext( const QgsPoint& point, QgsPoint& minDistPoint, int& afterVertex, double* leftOf = 0 ); + double closestSegmentWithContext( const QgsPoint& point, QgsPoint& minDistPoint, int& afterVertex, double* leftOf = 0, double epsilon = DEFAULT_SEGMENT_EPSILON ); /**Adds a new ring to this geometry. This makes only sense for polygon and multipolygons. @return 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index 5bb220e7ca6b..6f4d2e32027c 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -498,7 +498,7 @@ void QgsMapLayer::setSubLayerVisibility( QString name, bool vis ) // NOOP } -const QgsCoordinateReferenceSystem& QgsMapLayer::crs() +const QgsCoordinateReferenceSystem& QgsMapLayer::crs() const { return *mCRS; } diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h index ffa4191856e8..9e2ca5e5206b 100644 --- a/src/core/qgsmaplayer.h +++ b/src/core/qgsmaplayer.h @@ -221,7 +221,7 @@ class CORE_EXPORT QgsMapLayer : public QObject /** Returns layer's spatial reference system @note This was introduced in QGIS 1.4 */ - const QgsCoordinateReferenceSystem& crs(); + const QgsCoordinateReferenceSystem& crs() const; /** Returns layer's spatial reference system @note This method is here for API compatibility diff --git a/src/core/qgspoint.cpp b/src/core/qgspoint.cpp index 5d5474ea81b6..9a80651f0e14 100644 --- a/src/core/qgspoint.cpp +++ b/src/core/qgspoint.cpp @@ -245,7 +245,7 @@ int QgsPoint::onSegment( const QgsPoint& a, const QgsPoint& b ) const return 2; } -double QgsPoint::sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint ) const +double QgsPoint::sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint, double epsilon ) const { double nx, ny; //normal vector @@ -273,7 +273,7 @@ double QgsPoint::sqrDistToSegment( double x1, double y1, double x2, double y2, Q double dist = sqrDist( minDistPoint ); //prevent rounding errors if the point is directly on the segment - if ( doubleNear( dist, 0.0, 0.00000001 ) ) + if ( doubleNear( dist, 0.0, epsilon ) ) { minDistPoint.setX( m_x ); minDistPoint.setY( m_y ); diff --git a/src/core/qgspoint.h b/src/core/qgspoint.h index e64fdedb2152..e44516184cf7 100644 --- a/src/core/qgspoint.h +++ b/src/core/qgspoint.h @@ -18,6 +18,8 @@ #ifndef QGSPOINT_H #define QGSPOINT_H +#include + #include #include #include @@ -146,7 +148,7 @@ class CORE_EXPORT QgsPoint /**Returns the minimum distance between this point and a segment @note added in QGIS 1.5*/ - double sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint ) const; + double sqrDistToSegment( double x1, double y1, double x2, double y2, QgsPoint& minDistPoint, double epsilon = DEFAULT_SEGMENT_EPSILON ) const; /**Calculates azimut between this point and other one (clockwise in degree, starting from north) @note: this function has been added in version 1.7*/ diff --git a/src/core/qgsvectorlayer.cpp b/src/core/qgsvectorlayer.cpp index 6cef235fe64e..5a7fd3c14202 100644 --- a/src/core/qgsvectorlayer.cpp +++ b/src/core/qgsvectorlayer.cpp @@ -4285,7 +4285,7 @@ void QgsVectorLayer::snapToGeometry( const QgsPoint& startPoint, { if ( geometryType() != QGis::Point ) // cannot snap to segment for points/multipoints { - sqrDistSegmentSnap = geom->closestSegmentWithContext( startPoint, snappedPoint, afterVertex ); + sqrDistSegmentSnap = geom->closestSegmentWithContext( startPoint, snappedPoint, afterVertex, NULL, crs().geographicFlag() ? 1e-12 : 1e-8 ); if ( sqrDistSegmentSnap < sqrSnappingTolerance ) { @@ -4301,7 +4301,6 @@ void QgsVectorLayer::snapToGeometry( const QgsPoint& startPoint, } } } - } int QgsVectorLayer::insertSegmentVerticesForSnap( const QList& snapResults ) diff --git a/src/gui/qgsprojectionselector.h b/src/gui/qgsprojectionselector.h index d9f47edfd100..ba6494f92ea1 100644 --- a/src/gui/qgsprojectionselector.h +++ b/src/gui/qgsprojectionselector.h @@ -184,7 +184,6 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio //! Has the User Projection List been populated? bool mUserProjListDone; - bool mSkipFirstRecent; //! Has the Recent Projection List been populated? bool mRecentProjListDone; @@ -193,6 +192,8 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio int mSearchColumn; QString mSearchValue; + bool mSkipFirstRecent; + //! The set of OGC WMS CRSs that want to be applied to this widget QSet mCrsFilter;