From 66b0e59fb993050018ab60c61b816820b2c53c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Tue, 20 Mar 2018 19:08:04 +0100 Subject: [PATCH 1/6] Fix snapping on invisible geometry --- python/core/qgspointlocator.sip.in | 8 +- python/core/qgssnappingutils.sip.in | 11 +- python/gui/qgsmapcanvassnappingutils.sip.in | 1 + resources/qgis_global_settings.ini | 3 + src/app/qgsoptions.cpp | 2 + .../layertree/qgslayertreemodellegendnode.cpp | 1 + src/core/qgspointlocator.cpp | 45 ++++ src/core/qgspointlocator.h | 15 +- src/core/qgssnappingutils.cpp | 20 +- src/core/qgssnappingutils.h | 15 +- src/gui/qgsmapcanvassnappingutils.cpp | 10 +- src/gui/qgsmapcanvassnappingutils.h | 3 + src/ui/qgsoptionsbase.ui | 217 +++++++++--------- tests/src/core/testqgssnappingutils.cpp | 47 +++- 14 files changed, 273 insertions(+), 125 deletions(-) diff --git a/python/core/qgspointlocator.sip.in b/python/core/qgspointlocator.sip.in index 5f029a419b86..c6d243eddd75 100644 --- a/python/core/qgspointlocator.sip.in +++ b/python/core/qgspointlocator.sip.in @@ -71,6 +71,13 @@ Get extent of the area point locator covers - if null then it caches the whole l Configure extent - if not null, it will index only that area .. versionadded:: 2.14 +%End + + void setRenderContext( const QgsRenderContext &context ); +%Docstring +Configure render context - if not null, it will use to index only visible feature + +.. versionadded:: 3.2 %End enum Type @@ -199,7 +206,6 @@ Override of edgesInRect that construct rectangle from a center point and toleran find out if the point is in any polygons %End - int cachedGeometryCount() const; %Docstring Return how many geometries are cached in the index diff --git a/python/core/qgssnappingutils.sip.in b/python/core/qgssnappingutils.sip.in index 80715ed0275f..06fd433a7572 100644 --- a/python/core/qgssnappingutils.sip.in +++ b/python/core/qgssnappingutils.sip.in @@ -35,7 +35,7 @@ which keeps the configuration in sync with map canvas (e.g. current view, active %End public: - QgsSnappingUtils( QObject *parent /TransferThis/ = 0 ); + QgsSnappingUtils( QObject *parent /TransferThis/ = 0, bool enableSnappingForInvisibleFeature = true ); %Docstring Constructor for QgsSnappingUtils %End @@ -139,6 +139,15 @@ Get extra information about the instance QgsSnappingConfig config() const; %Docstring The snapping configuration controls the behavior of this object +%End + + void setEnableSnappingForInvisibleFeature( bool enable ); +%Docstring +Set if invisible features must be snapped or not. + +:param enable: Enable or not this feature + +.. versionadded:: 3.2 %End public slots: diff --git a/python/gui/qgsmapcanvassnappingutils.sip.in b/python/gui/qgsmapcanvassnappingutils.sip.in index 9a4b2e898f83..ccd1e12238c0 100644 --- a/python/gui/qgsmapcanvassnappingutils.sip.in +++ b/python/gui/qgsmapcanvassnappingutils.sip.in @@ -9,6 +9,7 @@ + class QgsMapCanvasSnappingUtils : QgsSnappingUtils { %Docstring diff --git a/resources/qgis_global_settings.ini b/resources/qgis_global_settings.ini index b2053b0de1f3..db89c2c58e8e 100644 --- a/resources/qgis_global_settings.ini +++ b/resources/qgis_global_settings.ini @@ -20,6 +20,9 @@ digitizing\default_snap_type=1 # 2 = Project units digitizing\default_snapping_tolerance_unit=1 +# Snap on invisble feature +digitizing\snap_invisible_feature=false + # Default XYZ tile servers to include connections-xyz\OpenStreetMap\authcfg= connections-xyz\OpenStreetMap\password= diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 804aec5857ba..0384d408ce04 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -971,6 +971,7 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QListsetColor( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_color" ), QColor( Qt::magenta ) ).value() ); mSnappingTooltipsCheckbox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_tooltip" ), false ).toBool() ); + mEnableSnappingOnInvisibleFeatureCheckbox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() ); //vertex marker mMarkersOnlyForSelectedCheckBox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/marker_only_for_selected" ), true ).toBool() ); @@ -1483,6 +1484,7 @@ void QgsOptions::saveOptions() mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_color" ), mSnappingMarkerColorButton->color() ); mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_tooltip" ), mSnappingTooltipsCheckbox->isChecked() ); + mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), mEnableSnappingOnInvisibleFeatureCheckbox->isChecked() ); mSettings->setValue( QStringLiteral( "/qgis/digitizing/marker_only_for_selected" ), mMarkersOnlyForSelectedCheckBox->isChecked() ); diff --git a/src/core/layertree/qgslayertreemodellegendnode.cpp b/src/core/layertree/qgslayertreemodellegendnode.cpp index bea2c2276971..528862456030 100644 --- a/src/core/layertree/qgslayertreemodellegendnode.cpp +++ b/src/core/layertree/qgslayertreemodellegendnode.cpp @@ -331,6 +331,7 @@ bool QgsSymbolLegendNode::setData( const QVariant &value, int role ) vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked ); emit dataChanged(); + emit vlayer->styleChanged(); vlayer->triggerRepaint(); diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index 8f64fd377721..6929d28bfd99 100644 --- a/src/core/qgspointlocator.cpp +++ b/src/core/qgspointlocator.cpp @@ -21,6 +21,7 @@ #include "qgswkbptr.h" #include "qgis.h" #include "qgslogger.h" +#include "qgsrenderer.h" #include @@ -636,6 +637,9 @@ QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateRefe connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted ); connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged ); connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsPointLocator::destroyIndex ); + connect( mLayer, &QgsVectorLayer::rendererChanged, this, &QgsPointLocator::destroyIndex ); + connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); + connect( mLayer, &QgsVectorLayer::layerModified, this, &QgsPointLocator::destroyIndex ); } @@ -661,6 +665,12 @@ void QgsPointLocator::setExtent( const QgsRectangle *extent ) destroyIndex(); } +void QgsPointLocator::setRenderContext( const QgsRenderContext &context ) +{ + mContext = std::unique_ptr( new QgsRenderContext( context ) ); + + destroyIndex(); +} bool QgsPointLocator::init( int maxFeaturesToIndex ) { @@ -686,6 +696,7 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) QgsFeatureRequest request; request.setSubsetOfAttributes( QgsAttributeList() ); + if ( mExtent ) { QgsRectangle rect = *mExtent; @@ -704,13 +715,40 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) } request.setFilterRect( rect ); } + + bool filter = false; + std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr ); + QgsRenderContext *ctx = nullptr; + if ( mContext ) + { + mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer ); + ctx = mContext.get(); + if ( renderer ) + { + // setup scale for scale dependent visibility (rule based) + renderer->startRender( *ctx, mLayer->fields() ); + filter = renderer->capabilities() & QgsFeatureRenderer::Filter; + request.setSubsetOfAttributes( renderer->usedAttributes( *ctx ), mLayer->fields() ); + } + } + QgsFeatureIterator fi = mLayer->getFeatures( request ); int indexedCount = 0; + while ( fi.nextFeature( f ) ) { if ( !f.hasGeometry() ) continue; + if ( ctx && renderer ) + { + ctx->expressionContext().setFeature( f ); + if ( filter && !renderer->willRenderFeature( f, *ctx ) ) + { + continue; + } + } + if ( mTransform.isValid() ) { try @@ -761,6 +799,12 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) QgsPointLocator_Stream stream( dataList ); mRTree = RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity, leafCapacity, dimension, variant, indexId ); + + if ( ctx && renderer ) + { + renderer->stopRender( *ctx ); + renderer.release(); + } return true; } @@ -832,6 +876,7 @@ void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid ) mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid ); delete mGeoms.take( fid ); } + } void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom ) diff --git a/src/core/qgspointlocator.h b/src/core/qgspointlocator.h index a7abcaf4a451..fa6a05a8282f 100644 --- a/src/core/qgspointlocator.h +++ b/src/core/qgspointlocator.h @@ -18,12 +18,15 @@ class QgsPointXY; class QgsVectorLayer; +class QgsFeatureRenderer; +class QgsRenderContext; #include "qgis_core.h" #include "qgsfeature.h" #include "qgspointxy.h" #include "qgscoordinatereferencesystem.h" #include "qgscoordinatetransform.h" +#include class QgsPointLocator_VisitorNearestVertex; class QgsPointLocator_VisitorNearestEdge; @@ -92,6 +95,12 @@ class CORE_EXPORT QgsPointLocator : public QObject */ void setExtent( const QgsRectangle *extent ); + /** + * Configure render context - if not null, it will use to index only visible feature + * \since QGIS 3.2 + */ + void setRenderContext( const QgsRenderContext &context ); + /** * The type of a snap result or the filter type for a snap request. */ @@ -251,8 +260,6 @@ class CORE_EXPORT QgsPointLocator : public QObject //! find out if the point is in any polygons MatchList pointInPolygon( const QgsPointXY &point ); - // - /** * Return how many geometries are cached in the index * \since QGIS 2.14 @@ -278,11 +285,15 @@ class CORE_EXPORT QgsPointLocator : public QObject //! flag whether the layer is currently empty (i.e. mRTree is null but it is not necessary to rebuild it) bool mIsEmptyLayer; + QgsFeatureIds mFeatureIds; + //! R-tree containing spatial index QgsCoordinateTransform mTransform; QgsVectorLayer *mLayer = nullptr; QgsRectangle *mExtent = nullptr; + std::unique_ptr mContext; + friend class QgsPointLocator_VisitorNearestVertex; friend class QgsPointLocator_VisitorNearestEdge; friend class QgsPointLocator_VisitorArea; diff --git a/src/core/qgssnappingutils.cpp b/src/core/qgssnappingutils.cpp index aabdb60ddd03..03bab01556a7 100644 --- a/src/core/qgssnappingutils.cpp +++ b/src/core/qgssnappingutils.cpp @@ -19,10 +19,12 @@ #include "qgsproject.h" #include "qgsvectorlayer.h" #include "qgslogger.h" +#include "qgsrenderer.h" -QgsSnappingUtils::QgsSnappingUtils( QObject *parent ) +QgsSnappingUtils::QgsSnappingUtils( QObject *parent, bool enableSnappingForInvisibleFeature ) : QObject( parent ) , mSnappingConfig( QgsProject::instance() ) + , mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature ) { } @@ -92,7 +94,6 @@ bool QgsSnappingUtils::isIndexPrepared( QgsVectorLayer *vl, const QgsRectangle & return ( mStrategy == IndexHybrid || mStrategy == IndexExtent ) && loc->hasIndex() && ( !loc->extent() || loc->extent()->contains( aoi ) ); // the index - even if it exists - is not suitable } - static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY &pt, const QgsPointLocator::MatchList &segments ) { if ( segments.isEmpty() ) @@ -156,9 +157,9 @@ static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP ); } - static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointLocator::Match &candidateMatch, double maxDistance ) { + // is candidate match relevant? if ( !candidateMatch.isValid() || candidateMatch.distance() > maxDistance ) return; @@ -174,7 +175,6 @@ static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointL bestMatch = candidateMatch; // the other match is better! } - static void _updateBestMatch( QgsPointLocator::Match &bestMatch, const QgsPointXY &pointMap, QgsPointLocator *loc, QgsPointLocator::Types type, double tolerance, QgsPointLocator::MatchFilter *filter ) { if ( type & QgsPointLocator::Vertex ) @@ -329,7 +329,6 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPointXY &pointMap, return QgsPointLocator::Match(); } - void QgsSnappingUtils::prepareIndex( const QList &layers ) { if ( mIsIndexing ) @@ -341,6 +340,7 @@ void QgsSnappingUtils::prepareIndex( const QList &layers Q_FOREACH ( const LayerAndAreaOfInterest &entry, layers ) { QgsVectorLayer *vl = entry.first; + if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull ) continue; @@ -359,7 +359,12 @@ void QgsSnappingUtils::prepareIndex( const QList &layers QgsVectorLayer *vl = entry.first; QTime tt; tt.start(); + QgsPointLocator *loc = locatorForLayer( vl ); + + if ( !mEnableSnappingForInvisibleFeature ) + loc->setRenderContext( QgsRenderContext::fromMapSettings( mMapSettings ) ); + if ( mStrategy == IndexExtent ) { QgsRectangle rect( mMapSettings.extent() ); @@ -428,6 +433,11 @@ QgsSnappingConfig QgsSnappingUtils::config() const return mSnappingConfig; } +void QgsSnappingUtils::setEnableSnappingForInvisibleFeature( bool enable ) +{ + mEnableSnappingForInvisibleFeature = enable; +} + void QgsSnappingUtils::setConfig( const QgsSnappingConfig &config ) { if ( mSnappingConfig == config ) diff --git a/src/core/qgssnappingutils.h b/src/core/qgssnappingutils.h index f956c07d2df1..75b7c3e9c0f4 100644 --- a/src/core/qgssnappingutils.h +++ b/src/core/qgssnappingutils.h @@ -53,7 +53,7 @@ class CORE_EXPORT QgsSnappingUtils : public QObject public: //! Constructor for QgsSnappingUtils - QgsSnappingUtils( QObject *parent SIP_TRANSFERTHIS = nullptr ); + QgsSnappingUtils( QObject *parent SIP_TRANSFERTHIS = nullptr, bool enableSnappingForInvisibleFeature = true ); ~QgsSnappingUtils() override; // main actions @@ -159,6 +159,15 @@ class CORE_EXPORT QgsSnappingUtils : public QObject */ QgsSnappingConfig config() const; + /** + * Set if invisible features must be snapped or not. + * + * \param enable Enable or not this feature + * + * \since QGIS 3.2 + */ + void setEnableSnappingForInvisibleFeature( bool enable ); + public slots: /** @@ -242,6 +251,10 @@ class CORE_EXPORT QgsSnappingUtils : public QObject //! internal flag that an indexing process is going on. Prevents starting two processes in parallel. bool mIsIndexing = false; + + //! Disable or not the snapping on all features. By default is always true except for non visible features on map canvas. + bool mEnableSnappingForInvisibleFeature = true; + }; diff --git a/src/gui/qgsmapcanvassnappingutils.cpp b/src/gui/qgsmapcanvassnappingutils.cpp index 978f271be538..3b119b8e9b3e 100644 --- a/src/gui/qgsmapcanvassnappingutils.cpp +++ b/src/gui/qgsmapcanvassnappingutils.cpp @@ -16,12 +16,13 @@ #include "qgsmapcanvas.h" #include "qgsvectorlayer.h" +#include "qgssettings.h" #include #include QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent ) - : QgsSnappingUtils( parent ) + : QgsSnappingUtils( parent, QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() ) , mCanvas( canvas ) { @@ -30,6 +31,7 @@ QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObj connect( canvas, &QgsMapCanvas::layersChanged, this, &QgsMapCanvasSnappingUtils::canvasMapSettingsChanged ); connect( canvas, &QgsMapCanvas::currentLayerChanged, this, &QgsMapCanvasSnappingUtils::canvasCurrentLayerChanged ); connect( canvas, &QgsMapCanvas::transformContextChanged, this, &QgsMapCanvasSnappingUtils::canvasTransformContextChanged ); + connect( canvas, &QgsMapCanvas::mapToolSet, this, &QgsMapCanvasSnappingUtils::canvasMapToolChanged ); canvasMapSettingsChanged(); canvasCurrentLayerChanged(); } @@ -37,6 +39,7 @@ QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObj void QgsMapCanvasSnappingUtils::canvasMapSettingsChanged() { setMapSettings( mCanvas->mapSettings() ); + setEnableSnappingForInvisibleFeature( QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() ); } void QgsMapCanvasSnappingUtils::canvasTransformContextChanged() @@ -51,6 +54,11 @@ void QgsMapCanvasSnappingUtils::canvasCurrentLayerChanged() setCurrentLayer( qobject_cast( mCanvas->currentLayer() ) ); } +void QgsMapCanvasSnappingUtils::canvasMapToolChanged() +{ + setEnableSnappingForInvisibleFeature( QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() ); +} + void QgsMapCanvasSnappingUtils::prepareIndexStarting( int count ) { QApplication::setOverrideCursor( Qt::WaitCursor ); diff --git a/src/gui/qgsmapcanvassnappingutils.h b/src/gui/qgsmapcanvassnappingutils.h index 39308530fc05..74f4e270a608 100644 --- a/src/gui/qgsmapcanvassnappingutils.h +++ b/src/gui/qgsmapcanvassnappingutils.h @@ -18,6 +18,8 @@ #include "qgssnappingutils.h" #include "qgis_gui.h" +#include "qgsmaptool.h" + class QgsMapCanvas; class QProgressDialog; @@ -42,6 +44,7 @@ class GUI_EXPORT QgsMapCanvasSnappingUtils : public QgsSnappingUtils void canvasMapSettingsChanged(); void canvasTransformContextChanged(); void canvasCurrentLayerChanged(); + void canvasMapToolChanged(); private: QgsMapCanvas *mCanvas = nullptr; diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 509086ba1442..a47824f07e44 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -320,7 +320,7 @@ - 7 + 8 @@ -349,8 +349,8 @@ 0 0 - 843 - 839 + 411 + 662 @@ -654,12 +654,12 @@ - - Modeless data source manager dialog - A modeless dialog allows you to interact with QGIS main window and dialogs. + + Modeless data source manager dialog + @@ -1040,8 +1040,8 @@ 0 0 - 843 - 1110 + 437 + 1011 @@ -1576,8 +1576,8 @@ 0 0 - 857 - 826 + 443 + 312 @@ -1743,8 +1743,8 @@ 0 0 - 857 - 826 + 393 + 648 @@ -2111,8 +2111,8 @@ 0 0 - 843 - 1110 + 541 + 866 @@ -2862,8 +2862,8 @@ 0 0 - 857 - 826 + 449 + 195 @@ -3119,8 +3119,8 @@ 0 0 - 857 - 826 + 515 + 527 @@ -3563,8 +3563,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 857 - 826 + 124 + 230 @@ -3731,8 +3731,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 572 - 889 + 862 + 838 @@ -3954,39 +3954,75 @@ The bigger the number, the faster zooming with the mouse wheel will be. Snapping - - - - - + + Enable snapping by default - - + + - Display main dialog as (restart required) + Default snap mode - - + + + + Qt::Horizontal + + + + 273 + 19 + + + + + + + + + 0 + 0 + + + + + + - Snapping marker color + Default snapping tolerance - - - Default snap mode + + + Qt::Horizontal + + + + 241 + 20 + + + + + + + + 5 + + + 99999999.989999994635582 - + @@ -4006,43 +4042,37 @@ The bigger the number, the faster zooming with the mouse wheel will be. - - - - Qt::Horizontal - - - - 61 - 20 - + + + + Search radius for vertex edits - + - + Qt::Horizontal - 241 + 61 20 - - - - - 0 - 0 - + + + + 5 + + + 99999999.989999994635582 - + @@ -4056,60 +4086,37 @@ The bigger the number, the faster zooming with the mouse wheel will be. - - - - 5 - - - 99999999.989999994635582 - - - - - - - 5 - - - 99999999.989999994635582 + + + + Display main dialog as (restart required) - - - - Qt::Horizontal - - - - 311 - 20 - - - - - + - - + + - Search radius for vertex edits + Snapping marker color - - + + + + + - Default snapping tolerance + Show snapping tooltips - - + + - Show snapping tooltips + Enable snapping on invisible features (not shown on the map canvas) @@ -4326,8 +4333,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 509 - 623 + 390 + 539 @@ -4595,8 +4602,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 435 - 402 + 862 + 838 @@ -4764,8 +4771,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 655 - 768 + 862 + 838 @@ -5533,7 +5540,6 @@ The bigger the number, the faster zooming with the mouse wheel will be. mSearchRadiusVertexEditComboBox mSnappingMainDialogComboBox mSnappingMarkerColorButton - mSnappingTooltipsCheckbox mMarkersOnlyForSelectedCheckBox mMarkerStyleComboBox mMarkerSizeSpinBox @@ -5609,7 +5615,6 @@ The bigger the number, the faster zooming with the mouse wheel will be. - diff --git a/tests/src/core/testqgssnappingutils.cpp b/tests/src/core/testqgssnappingutils.cpp index f676e41251bb..6dbae201f3f0 100644 --- a/tests/src/core/testqgssnappingutils.cpp +++ b/tests/src/core/testqgssnappingutils.cpp @@ -24,7 +24,8 @@ #include "qgsproject.h" #include "qgssnappingutils.h" #include "qgssnappingconfig.h" - +#include "qgscategorizedsymbolrenderer.h" +#include "qgssettings.h" struct FilterExcludePoint : public QgsPointLocator::MatchFilter { @@ -44,6 +45,7 @@ class TestQgsSnappingUtils : public QObject private: QgsVectorLayer *mVL = nullptr; + QgsFeature f1, f2; private slots: void initTestCase() @@ -60,16 +62,29 @@ class TestQgsSnappingUtils : public QObject // \ | // \| // + (1,0) - mVL = new QgsVectorLayer( QStringLiteral( "Polygon" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) ); - QgsFeature ff( 0 ); + mVL = new QgsVectorLayer( QStringLiteral( "Polygon?field=fld:int" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) ); + int idx = mVL->fields().indexFromName( QStringLiteral( "fld" ) ); + QVERIFY( idx != -1 ); + f1.initAttributes( 1 ); + f2.initAttributes( 1 ); + QgsPolygonXY polygon; QgsPolylineXY polyline; polyline << QgsPointXY( 0, 1 ) << QgsPointXY( 1, 0 ) << QgsPointXY( 1, 1 ) << QgsPointXY( 0, 1 ); polygon << polyline; QgsGeometry polygonGeom = QgsGeometry::fromPolygonXY( polygon ); - ff.setGeometry( polygonGeom ); + f1.setGeometry( polygonGeom ); + f1.setAttribute( idx, QVariant( 2 ) ); + + polyline << QgsPointXY( 10, 11 ) << QgsPointXY( 11, 10 ) << QgsPointXY( 11, 11 ) << QgsPointXY( 10, 11 ); + polygon << polyline; + polygonGeom = QgsGeometry::fromPolygonXY( polygon ); + f2.setGeometry( polygonGeom ); + f2.setAttribute( idx, QVariant( 20 ) ); QgsFeatureList flist; - flist << ff; + flist << f1 << f2; + + mVL->dataProvider()->addFeatures( flist ); QgsProject::instance()->addMapLayer( mVL ); @@ -82,6 +97,13 @@ class TestQgsSnappingUtils : public QObject void testSnapModeCurrent() { + QgsSymbol *s1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ); + QgsSymbol *s2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ); + QgsRendererCategory c1( 2, s1, "f1", true ); + QgsRendererCategory c2( 20, s2, "f2", false ); + QgsCategoryList cl; + cl << c1 << c2; + QgsMapSettings mapSettings; mapSettings.setOutputSize( QSize( 100, 100 ) ); mapSettings.setExtent( QgsRectangle( 0, 0, 1, 1 ) ); @@ -89,6 +111,7 @@ class TestQgsSnappingUtils : public QObject QgsSnappingUtils u; u.setMapSettings( mapSettings ); + u.setEnableSnappingForInvisibleFeature( false ); u.setCurrentLayer( mVL ); // first try with no snapping enabled @@ -99,7 +122,7 @@ class TestQgsSnappingUtils : public QObject snappingConfig.setMode( QgsSnappingConfig::ActiveLayer ); u.setConfig( snappingConfig ); - QgsPointLocator::Match m0 = u.snapToMap( QPoint( 100, 100 ) ); + QgsPointLocator::Match m0 = u.snapToMap( QPoint( 2, 2 ) ); QVERIFY( !m0.isValid() ); QVERIFY( !m0.hasVertex() ); @@ -108,10 +131,16 @@ class TestQgsSnappingUtils : public QObject snappingConfig.setType( QgsSnappingConfig::Vertex ); u.setConfig( snappingConfig ); - QgsPointLocator::Match m = u.snapToMap( QPoint( 100, 100 ) ); + QgsPointLocator::Match m = u.snapToMap( QPoint( 11, 11 ) ); + QVERIFY( !m.isValid() ); + QVERIFY( !m.hasVertex() ); + + u.setEnableSnappingForInvisibleFeature( true ); + mVL->styleChanged(); + m = u.snapToMap( QPoint( 2, 2 ) ); QVERIFY( m.isValid() ); QVERIFY( m.hasVertex() ); - QCOMPARE( m.point(), QgsPointXY( 1, 0 ) ); + QCOMPARE( m.point(), QgsPointXY( 0, 1 ) ); QgsPointLocator::Match m2 = u.snapToMap( QPoint( 0, 100 ) ); QVERIFY( !m2.isValid() ); @@ -127,6 +156,8 @@ class TestQgsSnappingUtils : public QObject FilterExcludePoint myFilter( QgsPointXY( 1, 0 ) ); QgsPointLocator::Match m3 = u.snapToMap( QPoint( 100, 100 ), &myFilter ); QVERIFY( !m3.isValid() ); + + } void testSnapModeAll() From 5e5fdf948979a87dcab85eda50dd8415e9f1bef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Tue, 24 Apr 2018 17:44:52 +0200 Subject: [PATCH 2/6] fix martin's review --- src/core/layertree/qgslayertreemodellegendnode.cpp | 2 +- src/core/qgspointlocator.cpp | 8 +++----- src/core/qgspointlocator.h | 1 - tests/src/core/testqgssnappingutils.cpp | 1 - 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/core/layertree/qgslayertreemodellegendnode.cpp b/src/core/layertree/qgslayertreemodellegendnode.cpp index 528862456030..e5d0cfe993da 100644 --- a/src/core/layertree/qgslayertreemodellegendnode.cpp +++ b/src/core/layertree/qgslayertreemodellegendnode.cpp @@ -331,7 +331,7 @@ bool QgsSymbolLegendNode::setData( const QVariant &value, int role ) vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked ); emit dataChanged(); - emit vlayer->styleChanged(); + vlayer->emitStyleChanged(); vlayer->triggerRepaint(); diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index 6929d28bfd99..6f42222cc5e8 100644 --- a/src/core/qgspointlocator.cpp +++ b/src/core/qgspointlocator.cpp @@ -637,9 +637,6 @@ QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateRefe connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted ); connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged ); connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsPointLocator::destroyIndex ); - connect( mLayer, &QgsVectorLayer::rendererChanged, this, &QgsPointLocator::destroyIndex ); - connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); - connect( mLayer, &QgsVectorLayer::layerModified, this, &QgsPointLocator::destroyIndex ); } @@ -669,6 +666,7 @@ void QgsPointLocator::setRenderContext( const QgsRenderContext &context ) { mContext = std::unique_ptr( new QgsRenderContext( context ) ); + connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); destroyIndex(); } @@ -740,10 +738,10 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) if ( !f.hasGeometry() ) continue; - if ( ctx && renderer ) + if ( filter && ctx && renderer ) { ctx->expressionContext().setFeature( f ); - if ( filter && !renderer->willRenderFeature( f, *ctx ) ) + if ( !renderer->willRenderFeature( f, *ctx ) ) { continue; } diff --git a/src/core/qgspointlocator.h b/src/core/qgspointlocator.h index fa6a05a8282f..ed379f433287 100644 --- a/src/core/qgspointlocator.h +++ b/src/core/qgspointlocator.h @@ -285,7 +285,6 @@ class CORE_EXPORT QgsPointLocator : public QObject //! flag whether the layer is currently empty (i.e. mRTree is null but it is not necessary to rebuild it) bool mIsEmptyLayer; - QgsFeatureIds mFeatureIds; //! R-tree containing spatial index QgsCoordinateTransform mTransform; diff --git a/tests/src/core/testqgssnappingutils.cpp b/tests/src/core/testqgssnappingutils.cpp index 6dbae201f3f0..74b84a4aa513 100644 --- a/tests/src/core/testqgssnappingutils.cpp +++ b/tests/src/core/testqgssnappingutils.cpp @@ -136,7 +136,6 @@ class TestQgsSnappingUtils : public QObject QVERIFY( !m.hasVertex() ); u.setEnableSnappingForInvisibleFeature( true ); - mVL->styleChanged(); m = u.snapToMap( QPoint( 2, 2 ) ); QVERIFY( m.isValid() ); QVERIFY( m.hasVertex() ); From 43e905d7fcb4bbaf8851f5f1125d6fc9f9db48e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Wed, 25 Apr 2018 15:33:14 +0200 Subject: [PATCH 3/6] Add support for visible features in onFeatureAdded, onGeometryChanged and onAttributeValueChanged --- src/core/qgspointlocator.cpp | 33 +++++++++++++++++++++++++++++++++ src/core/qgspointlocator.h | 1 + 2 files changed, 34 insertions(+) diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index 6f42222cc5e8..8cfcc6756472 100644 --- a/src/core/qgspointlocator.cpp +++ b/src/core/qgspointlocator.cpp @@ -636,6 +636,7 @@ QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateRefe connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded ); connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted ); connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged ); + connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged ); connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsPointLocator::destroyIndex ); } @@ -834,6 +835,30 @@ void QgsPointLocator::onFeatureAdded( QgsFeatureId fid ) if ( !f.hasGeometry() ) return; + std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr ); + QgsRenderContext *ctx = nullptr; + if ( mContext ) + { + mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer ); + ctx = mContext.get(); + if ( renderer && ctx ) + { + bool pass = false; + renderer->startRender( *ctx, mLayer->fields() ); + + ctx->expressionContext().setFeature( f ); + if ( !renderer->willRenderFeature( f, *ctx ) ) + { + pass = true; + } + + renderer->stopRender( *ctx ); + renderer.release(); + if ( pass ) + return; + } + } + if ( mTransform.isValid() ) { try @@ -884,6 +909,14 @@ void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &ge onFeatureAdded( fid ); } +void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value ) +{ + Q_UNUSED( idx ); + Q_UNUSED( value ); + onFeatureDeleted( fid ); + onFeatureAdded( fid ); +} + QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter ) { diff --git a/src/core/qgspointlocator.h b/src/core/qgspointlocator.h index ed379f433287..09fc877638d0 100644 --- a/src/core/qgspointlocator.h +++ b/src/core/qgspointlocator.h @@ -274,6 +274,7 @@ class CORE_EXPORT QgsPointLocator : public QObject void onFeatureAdded( QgsFeatureId fid ); void onFeatureDeleted( QgsFeatureId fid ); void onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom ); + void onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value ); private: //! Storage manager From 07caafe7c63a89420cbd9ad935e2fbd1349e67a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Wed, 25 Apr 2018 17:05:14 +0200 Subject: [PATCH 4/6] try disconnect/connect for destroyIndex --- python/core/qgspointlocator.sip.in | 2 +- src/core/qgspointlocator.cpp | 13 ++++++++++--- src/core/qgspointlocator.h | 2 +- src/core/qgssnappingutils.cpp | 5 ++++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/python/core/qgspointlocator.sip.in b/python/core/qgspointlocator.sip.in index c6d243eddd75..2ccc3b7e318a 100644 --- a/python/core/qgspointlocator.sip.in +++ b/python/core/qgspointlocator.sip.in @@ -73,7 +73,7 @@ Configure extent - if not null, it will index only that area .. versionadded:: 2.14 %End - void setRenderContext( const QgsRenderContext &context ); + void setRenderContext( const QgsRenderContext *context ); %Docstring Configure render context - if not null, it will use to index only visible feature diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index 8cfcc6756472..89517862594f 100644 --- a/src/core/qgspointlocator.cpp +++ b/src/core/qgspointlocator.cpp @@ -663,12 +663,19 @@ void QgsPointLocator::setExtent( const QgsRectangle *extent ) destroyIndex(); } -void QgsPointLocator::setRenderContext( const QgsRenderContext &context ) +void QgsPointLocator::setRenderContext( const QgsRenderContext *context ) { - mContext = std::unique_ptr( new QgsRenderContext( context ) ); + disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); - connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); destroyIndex(); + mContext.reset( nullptr ); + + if ( context ) + { + mContext = std::unique_ptr( new QgsRenderContext( *context ) ); + connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); + } + } bool QgsPointLocator::init( int maxFeaturesToIndex ) diff --git a/src/core/qgspointlocator.h b/src/core/qgspointlocator.h index 09fc877638d0..0c8f5a941de5 100644 --- a/src/core/qgspointlocator.h +++ b/src/core/qgspointlocator.h @@ -99,7 +99,7 @@ class CORE_EXPORT QgsPointLocator : public QObject * Configure render context - if not null, it will use to index only visible feature * \since QGIS 3.2 */ - void setRenderContext( const QgsRenderContext &context ); + void setRenderContext( const QgsRenderContext *context ); /** * The type of a snap result or the filter type for a snap request. diff --git a/src/core/qgssnappingutils.cpp b/src/core/qgssnappingutils.cpp index 03bab01556a7..b7c22fc16a11 100644 --- a/src/core/qgssnappingutils.cpp +++ b/src/core/qgssnappingutils.cpp @@ -363,7 +363,10 @@ void QgsSnappingUtils::prepareIndex( const QList &layers QgsPointLocator *loc = locatorForLayer( vl ); if ( !mEnableSnappingForInvisibleFeature ) - loc->setRenderContext( QgsRenderContext::fromMapSettings( mMapSettings ) ); + { + QgsRenderContext ctx = QgsRenderContext::fromMapSettings( mMapSettings ); + loc->setRenderContext( &ctx ); + } if ( mStrategy == IndexExtent ) { From 68f463e34d97ff4beb6f8a5300fe8c2464760695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Mon, 30 Apr 2018 10:50:04 +0200 Subject: [PATCH 5/6] Improve onFeatureAdded and onAttributeValueChanged --- src/core/qgspointlocator.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index 89517862594f..dc7a9fdc21ac 100644 --- a/src/core/qgspointlocator.cpp +++ b/src/core/qgspointlocator.cpp @@ -842,10 +842,11 @@ void QgsPointLocator::onFeatureAdded( QgsFeatureId fid ) if ( !f.hasGeometry() ) return; - std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr ); - QgsRenderContext *ctx = nullptr; if ( mContext ) { + std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr ); + QgsRenderContext *ctx = nullptr; + mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer ); ctx = mContext.get(); if ( renderer && ctx ) @@ -920,8 +921,11 @@ void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const { Q_UNUSED( idx ); Q_UNUSED( value ); - onFeatureDeleted( fid ); - onFeatureAdded( fid ); + if ( mContext ) + { + onFeatureDeleted( fid ); + onFeatureAdded( fid ); + } } From 9f3d571155e9268b16eb6ffa79a739baf3a28751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bartoletti?= Date: Mon, 30 Apr 2018 17:02:47 +0200 Subject: [PATCH 6/6] clean tests and add a new test for snapping on inivisble feature --- tests/src/core/testqgssnappingutils.cpp | 98 +++++++++++++++++++------ 1 file changed, 74 insertions(+), 24 deletions(-) diff --git a/tests/src/core/testqgssnappingutils.cpp b/tests/src/core/testqgssnappingutils.cpp index 74b84a4aa513..8c941aab36b5 100644 --- a/tests/src/core/testqgssnappingutils.cpp +++ b/tests/src/core/testqgssnappingutils.cpp @@ -26,6 +26,8 @@ #include "qgssnappingconfig.h" #include "qgscategorizedsymbolrenderer.h" #include "qgssettings.h" +#include "qgslayertree.h" +#include "qgslayertreemodel.h" struct FilterExcludePoint : public QgsPointLocator::MatchFilter { @@ -75,19 +77,13 @@ class TestQgsSnappingUtils : public QObject QgsGeometry polygonGeom = QgsGeometry::fromPolygonXY( polygon ); f1.setGeometry( polygonGeom ); f1.setAttribute( idx, QVariant( 2 ) ); - - polyline << QgsPointXY( 10, 11 ) << QgsPointXY( 11, 10 ) << QgsPointXY( 11, 11 ) << QgsPointXY( 10, 11 ); - polygon << polyline; - polygonGeom = QgsGeometry::fromPolygonXY( polygon ); - f2.setGeometry( polygonGeom ); - f2.setAttribute( idx, QVariant( 20 ) ); QgsFeatureList flist; - flist << f1 << f2; - + flist << f1; mVL->dataProvider()->addFeatures( flist ); QgsProject::instance()->addMapLayer( mVL ); + } void cleanupTestCase() @@ -97,13 +93,6 @@ class TestQgsSnappingUtils : public QObject void testSnapModeCurrent() { - QgsSymbol *s1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ); - QgsSymbol *s2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ); - QgsRendererCategory c1( 2, s1, "f1", true ); - QgsRendererCategory c2( 20, s2, "f2", false ); - QgsCategoryList cl; - cl << c1 << c2; - QgsMapSettings mapSettings; mapSettings.setOutputSize( QSize( 100, 100 ) ); mapSettings.setExtent( QgsRectangle( 0, 0, 1, 1 ) ); @@ -111,7 +100,6 @@ class TestQgsSnappingUtils : public QObject QgsSnappingUtils u; u.setMapSettings( mapSettings ); - u.setEnableSnappingForInvisibleFeature( false ); u.setCurrentLayer( mVL ); // first try with no snapping enabled @@ -122,7 +110,7 @@ class TestQgsSnappingUtils : public QObject snappingConfig.setMode( QgsSnappingConfig::ActiveLayer ); u.setConfig( snappingConfig ); - QgsPointLocator::Match m0 = u.snapToMap( QPoint( 2, 2 ) ); + QgsPointLocator::Match m0 = u.snapToMap( QPoint( 100, 100 ) ); QVERIFY( !m0.isValid() ); QVERIFY( !m0.hasVertex() ); @@ -131,15 +119,10 @@ class TestQgsSnappingUtils : public QObject snappingConfig.setType( QgsSnappingConfig::Vertex ); u.setConfig( snappingConfig ); - QgsPointLocator::Match m = u.snapToMap( QPoint( 11, 11 ) ); - QVERIFY( !m.isValid() ); - QVERIFY( !m.hasVertex() ); - - u.setEnableSnappingForInvisibleFeature( true ); - m = u.snapToMap( QPoint( 2, 2 ) ); + QgsPointLocator::Match m = u.snapToMap( QPoint( 100, 100 ) ); QVERIFY( m.isValid() ); QVERIFY( m.hasVertex() ); - QCOMPARE( m.point(), QgsPointXY( 0, 1 ) ); + QCOMPARE( m.point(), QgsPointXY( 1, 0 ) ); QgsPointLocator::Match m2 = u.snapToMap( QPoint( 0, 100 ) ); QVERIFY( !m2.isValid() ); @@ -155,8 +138,75 @@ class TestQgsSnappingUtils : public QObject FilterExcludePoint myFilter( QgsPointXY( 1, 0 ) ); QgsPointLocator::Match m3 = u.snapToMap( QPoint( 100, 100 ), &myFilter ); QVERIFY( !m3.isValid() ); + } + + void testSnapInvisible() + { + QgsCategorizedSymbolRenderer *renderer = new QgsCategorizedSymbolRenderer(); + renderer->setClassAttribute( QStringLiteral( "fld" ) ); + renderer->setSourceSymbol( QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ) ); + renderer->addCategory( QgsRendererCategory( "2", QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ), QStringLiteral( "2" ) ) ); + mVL->setRenderer( renderer ); + + //create legend with symbology nodes for categorized renderer + QgsLayerTree *root = new QgsLayerTree(); + QgsLayerTreeLayer *n = new QgsLayerTreeLayer( mVL ); + root->addChildNode( n ); + QgsLayerTreeModel *m = new QgsLayerTreeModel( root, nullptr ); + m->refreshLayerLegend( n ); + + //test that all nodes are initially checked + QList nodes = m->layerLegendNodes( n ); + QCOMPARE( nodes.length(), 1 ); + Q_FOREACH ( QgsLayerTreeModelLegendNode *ln, nodes ) + { + QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Checked ); + } + + + QgsMapSettings mapSettings; + mapSettings.setOutputSize( QSize( 100, 100 ) ); + mapSettings.setExtent( QgsRectangle( 0, 0, 1, 1 ) ); + QVERIFY( mapSettings.hasValidSettings() ); + QgsSnappingUtils u; + u.setMapSettings( mapSettings ); + u.setEnableSnappingForInvisibleFeature( false ); + u.setCurrentLayer( mVL ); + + // first try with no snapping enabled + QgsSnappingConfig snappingConfig = u.config(); + snappingConfig.setEnabled( false ); + snappingConfig.setTolerance( 10 ); + snappingConfig.setUnits( QgsTolerance::Pixels ); + snappingConfig.setMode( QgsSnappingConfig::ActiveLayer ); + u.setConfig( snappingConfig ); + + QgsPointLocator::Match m0 = u.snapToMap( QPoint( 2, 2 ) ); + QVERIFY( !m0.isValid() ); + QVERIFY( !m0.hasVertex() ); + + // now enable snapping + snappingConfig.setEnabled( true ); + snappingConfig.setType( QgsSnappingConfig::Vertex ); + u.setConfig( snappingConfig ); + QgsPointLocator::Match m5 = u.snapToMap( QPoint( 2, 2 ) ); + QVERIFY( m5.isValid() ); + QVERIFY( m5.hasVertex() ); + QCOMPARE( m5.point(), QgsPointXY( 0, 1 ) ); + + //uncheck all and test that all nodes are unchecked + static_cast< QgsSymbolLegendNode * >( nodes.at( 0 ) )->uncheckAllItems(); + Q_FOREACH ( QgsLayerTreeModelLegendNode *ln, nodes ) + { + QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Unchecked ); + } + mVL->dataChanged(); /* refresh index */ + + m5 = u.snapToMap( QPoint( 2, 2 ) ); + QVERIFY( !m5.isValid() ); + QVERIFY( !m5.hasVertex() ); } void testSnapModeAll()