diff --git a/python/core/qgspointlocator.sip.in b/python/core/qgspointlocator.sip.in index 5f029a419b86..2ccc3b7e318a 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..e5d0cfe993da 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(); + vlayer->emitStyleChanged(); vlayer->triggerRepaint(); diff --git a/src/core/qgspointlocator.cpp b/src/core/qgspointlocator.cpp index 8f64fd377721..dc7a9fdc21ac 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 @@ -635,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 ); } @@ -661,6 +663,20 @@ void QgsPointLocator::setExtent( const QgsRectangle *extent ) destroyIndex(); } +void QgsPointLocator::setRenderContext( const QgsRenderContext *context ) +{ + disconnect( 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 ) { @@ -686,6 +702,7 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) QgsFeatureRequest request; request.setSubsetOfAttributes( QgsAttributeList() ); + if ( mExtent ) { QgsRectangle rect = *mExtent; @@ -704,13 +721,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 ( filter && ctx && renderer ) + { + ctx->expressionContext().setFeature( f ); + if ( !renderer->willRenderFeature( f, *ctx ) ) + { + continue; + } + } + if ( mTransform.isValid() ) { try @@ -761,6 +805,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; } @@ -792,6 +842,31 @@ void QgsPointLocator::onFeatureAdded( QgsFeatureId fid ) if ( !f.hasGeometry() ) return; + 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 ) + { + 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 @@ -832,6 +907,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 ) @@ -841,6 +917,17 @@ 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 ); + if ( mContext ) + { + 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 a7abcaf4a451..0c8f5a941de5 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 @@ -267,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 @@ -278,11 +286,14 @@ 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; + //! 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..b7c22fc16a11 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,15 @@ void QgsSnappingUtils::prepareIndex( const QList &layers QgsVectorLayer *vl = entry.first; QTime tt; tt.start(); + QgsPointLocator *loc = locatorForLayer( vl ); + + if ( !mEnableSnappingForInvisibleFeature ) + { + QgsRenderContext ctx = QgsRenderContext::fromMapSettings( mMapSettings ); + loc->setRenderContext( &ctx ); + } + if ( mStrategy == IndexExtent ) { QgsRectangle rect( mMapSettings.extent() ); @@ -428,6 +436,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..8c941aab36b5 100644 --- a/tests/src/core/testqgssnappingutils.cpp +++ b/tests/src/core/testqgssnappingutils.cpp @@ -24,7 +24,10 @@ #include "qgsproject.h" #include "qgssnappingutils.h" #include "qgssnappingconfig.h" - +#include "qgscategorizedsymbolrenderer.h" +#include "qgssettings.h" +#include "qgslayertree.h" +#include "qgslayertreemodel.h" struct FilterExcludePoint : public QgsPointLocator::MatchFilter { @@ -44,6 +47,7 @@ class TestQgsSnappingUtils : public QObject private: QgsVectorLayer *mVL = nullptr; + QgsFeature f1, f2; private slots: void initTestCase() @@ -60,19 +64,26 @@ 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 ) ); QgsFeatureList flist; - flist << ff; + flist << f1; + mVL->dataProvider()->addFeatures( flist ); QgsProject::instance()->addMapLayer( mVL ); + } void cleanupTestCase() @@ -129,6 +140,75 @@ class TestQgsSnappingUtils : public QObject 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() { QgsMapSettings mapSettings;