From b484caee819e5effae111b544631f68a57757757 Mon Sep 17 00:00:00 2001 From: obrix Date: Fri, 13 Mar 2020 17:22:31 +0100 Subject: [PATCH] Add global config for snapping limit on scale (with action and icon to enable). Also use 0.0 for disabling value and display NULL to be more explicit. --- images/images.qrc | 1 + .../themes/default/mIconSnappingOnScale.svg | 158 ++++++++++++++++++ src/app/qgssnappinglayertreemodel.cpp | 24 ++- src/app/qgssnappingwidget.cpp | 82 +++++++++ src/app/qgssnappingwidget.h | 13 ++ src/core/qgssnappingconfig.cpp | 65 ++++++- src/core/qgssnappingconfig.h | 21 +++ src/core/qgssnappingutils.cpp | 12 +- 8 files changed, 364 insertions(+), 12 deletions(-) create mode 100644 images/themes/default/mIconSnappingOnScale.svg diff --git a/images/images.qrc b/images/images.qrc index 6827686bf75..ad7b5badee4 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -684,6 +684,7 @@ themes/default/mIconSnappingArea.svg themes/default/mIconSnappingCentroid.svg themes/default/mIconSnappingMiddle.svg + themes/default/mIconSnappingOnScale.svg themes/default/mIconSnappingVertex.svg themes/default/mIconSnappingSegment.svg themes/default/mIconTopologicalEditing.svg diff --git a/images/themes/default/mIconSnappingOnScale.svg b/images/themes/default/mIconSnappingOnScale.svg new file mode 100644 index 00000000000..f2869a29e39 --- /dev/null +++ b/images/themes/default/mIconSnappingOnScale.svg @@ -0,0 +1,158 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/qgssnappinglayertreemodel.cpp b/src/app/qgssnappinglayertreemodel.cpp index 76720a39e91..467e40da803 100644 --- a/src/app/qgssnappinglayertreemodel.cpp +++ b/src/app/qgssnappinglayertreemodel.cpp @@ -104,9 +104,10 @@ QWidget *QgsSnappingLayerDelegate::createEditor( QWidget *parent, const QStyleOp { QDoubleSpinBox *minLimitSp = new QDoubleSpinBox( parent ); minLimitSp->setDecimals( 5 ); - minLimitSp->setMinimum( -1.0 ); + minLimitSp->setMinimum( 0.0 ); minLimitSp->setMaximum( 99999999.990000 ); minLimitSp->setToolTip( tr( "Min Scale" ) ); + minLimitSp->setSpecialValueText("NULL"); return minLimitSp; } @@ -114,9 +115,10 @@ QWidget *QgsSnappingLayerDelegate::createEditor( QWidget *parent, const QStyleOp { QDoubleSpinBox *maxLimitSp = new QDoubleSpinBox( parent ); maxLimitSp->setDecimals( 5 ); - maxLimitSp->setMinimum( -1.0 ); + maxLimitSp->setMinimum( 0.0 ); maxLimitSp->setMaximum( 99999999.990000 ); maxLimitSp->setToolTip( tr( "Max Scale" ) ); + maxLimitSp->setSpecialValueText("NULL"); return maxLimitSp; } @@ -626,7 +628,14 @@ QVariant QgsSnappingLayerTreeModel::data( const QModelIndex &idx, int role ) con { if ( role == Qt::DisplayRole ) { - return QString::number( ls.minScale() ); + if( ls.minScale() <= 0.0) + { + return QString( "NULL" ); + } + else + { + return QString::number( ls.minScale() ); + } } if ( role == Qt::UserRole ) @@ -639,7 +648,14 @@ QVariant QgsSnappingLayerTreeModel::data( const QModelIndex &idx, int role ) con { if ( role == Qt::DisplayRole ) { - return QString::number( ls.maxScale() ); + if( ls.maxScale() <= 0.0 ) + { + return QString( "NULL" ); + } + else + { + return QString::number( ls.maxScale() ); + } } if ( role == Qt::UserRole ) diff --git a/src/app/qgssnappingwidget.cpp b/src/app/qgssnappingwidget.cpp index 88d2599aa1c..7946dc2b768 100644 --- a/src/app/qgssnappingwidget.cpp +++ b/src/app/qgssnappingwidget.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "qgisapp.h" #include "qgsapplication.h" @@ -173,6 +174,31 @@ QgsSnappingWidget::QgsSnappingWidget( QgsProject *project, QgsMapCanvas *canvas, mToleranceSpinBox->setObjectName( QStringLiteral( "SnappingToleranceSpinBox" ) ); connect( mToleranceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSnappingWidget::changeTolerance ); + mMinScaleSpinBox = new QDoubleSpinBox(); + mMinScaleSpinBox->setDecimals( 2 ); + mMinScaleSpinBox->setMaximum( 99999999.990000 ); + mMinScaleSpinBox->setMinimum( 0.0 ); + mMinScaleSpinBox->setToolTip( tr( "Min scale on which snapping is enabled" ) ); + mMinScaleSpinBox->setObjectName( QStringLiteral( "SnappingMinScaleSpinBox" ) ); + mMinScaleSpinBox->setSpecialValueText("NULL"); + connect( mMinScaleSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSnappingWidget::changeMinScale ); + + mMaxScaleSpinBox = new QDoubleSpinBox(); + mMaxScaleSpinBox->setDecimals( 2 ); + mMaxScaleSpinBox->setMaximum( 99999999.990000 ); + mMaxScaleSpinBox->setMinimum( 0.0 ); + mMaxScaleSpinBox->setToolTip( tr( "Max scale on which snapping is enabled" ) ); + mMaxScaleSpinBox->setObjectName( QStringLiteral( "SnappingMaxScaleSpinBox" ) ); + mMaxScaleSpinBox->setSpecialValueText("NULL"); + connect( mMaxScaleSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSnappingWidget::changeMaxScale ); + + + mLimitToScale = new QAction( tr( "Toggle Snapping limit on scale" ), this ); + mLimitToScale->setCheckable( true ); + mLimitToScale->setIcon( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingOnScale.svg" ) ) ); + mLimitToScale->setObjectName( QStringLiteral( "EnableSnappinLimitOnScaleAction" ) ); + connect( mLimitToScale, &QAction::toggled, this, &QgsSnappingWidget::changeLimitToScale ); + // units mUnitsComboBox = new QComboBox(); mUnitsComboBox->addItem( tr( "px" ), QgsTolerance::Pixels ); @@ -261,6 +287,10 @@ QgsSnappingWidget::QgsSnappingWidget( QgsProject *project, QgsMapCanvas *canvas, mTypeAction = tb->addWidget( mTypeButton ); mToleranceAction = tb->addWidget( mToleranceSpinBox ); mUnitAction = tb->addWidget( mUnitsComboBox ); + tb->addAction( mLimitToScale ); + mMinScaleAction = tb->addWidget( mMinScaleSpinBox ); + mMaxScaleAction = tb->addWidget( mMaxScaleSpinBox ); + tb->addAction( mTopologicalEditingAction ); tb->addAction( mIntersectionSnappingAction ); tb->addAction( mEnableTracingAction ); @@ -279,6 +309,12 @@ QgsSnappingWidget::QgsSnappingWidget( QgsProject *project, QgsMapCanvas *canvas, layout->addWidget( mTypeButton ); layout->addWidget( mToleranceSpinBox ); layout->addWidget( mUnitsComboBox ); + QToolButton *limitToScaleButton = new QToolButton(); + limitToScaleButton->addAction( mLimitToScale ); + limitToScaleButton->setDefaultAction( mLimitToScale ); + layout->addWidget( limitToScaleButton ); + layout->addWidget( mMinScaleSpinBox ); + layout->addWidget( mMaxScaleSpinBox ); QToolButton *topoButton = new QToolButton(); topoButton->addAction( mTopologicalEditingAction ); @@ -401,12 +437,29 @@ void QgsSnappingWidget::projectSnapSettingsChanged() mToleranceSpinBox->setValue( config.tolerance() ); } + if ( mMinScaleSpinBox->value() != config.minScale() ) + { + mMinScaleSpinBox->setValue( config.minScale() ); + } + + if ( mMaxScaleSpinBox->value() != config.maxScale() ) + { + mMaxScaleSpinBox->setValue( config.maxScale() ); + } + + mLimitToScale->setChecked(config.limitToScale()); + /*if( mLimitToScale->isChecked() != config.limitToScale() ) + { + mLimitToScale->setCheckState( config.limitToScale() ? Qt::Checked : Qt::Unchecked ); + }*/ + if ( config.intersectionSnapping() != mIntersectionSnappingAction->isChecked() ) { mIntersectionSnappingAction->setChecked( config.intersectionSnapping() ); } toggleSnappingWidgets( config.enabled() ); + } void QgsSnappingWidget::projectTopologicalEditingChanged() @@ -429,6 +482,9 @@ void QgsSnappingWidget::toggleSnappingWidgets( bool enabled ) mModeButton->setEnabled( enabled ); mTypeButton->setEnabled( enabled ); mToleranceSpinBox->setEnabled( enabled ); + mLimitToScale->setEnabled( enabled ); + mMinScaleSpinBox->setEnabled( enabled && mConfig.limitToScale() ); + mMaxScaleSpinBox->setEnabled( enabled && mConfig.limitToScale() ); mUnitsComboBox->setEnabled( enabled ); if ( mEditAdvancedConfigAction ) { @@ -448,6 +504,26 @@ void QgsSnappingWidget::changeTolerance( double tolerance ) mProject->setSnappingConfig( mConfig ); } +void QgsSnappingWidget::changeMinScale( double pMinScale ) +{ + mConfig.setMinScale( pMinScale ); + mProject->setSnappingConfig( mConfig ); +} + +void QgsSnappingWidget::changeMaxScale( double pMaxScale ) +{ + mConfig.setMaxScale( pMaxScale ); + mProject->setSnappingConfig( mConfig ); +} + +void QgsSnappingWidget::changeLimitToScale( bool enabled ) +{ + mConfig.setLimitToScale( enabled ); + mMinScaleSpinBox->setEnabled(mConfig.limitToScale()); + mMaxScaleSpinBox->setEnabled(mConfig.limitToScale()); + mProject->setSnappingConfig( mConfig ); +} + void QgsSnappingWidget::changeUnit( int idx ) { QgsTolerance::UnitType unit = static_cast( mUnitsComboBox->itemData( idx ).toInt() ); @@ -564,6 +640,9 @@ void QgsSnappingWidget::modeChanged() mToleranceAction->setVisible( !advanced ); mUnitAction->setVisible( !advanced ); mEditAdvancedConfigAction->setVisible( advanced ); + mMaxScaleAction->setVisible( advanced ); + mMinScaleAction->setVisible( advanced ); + mLimitToScale->setVisible( advanced ); } else { @@ -574,6 +653,9 @@ void QgsSnappingWidget::modeChanged() { mAdvancedConfigWidget->setVisible( advanced ); } + mMinScaleSpinBox->setVisible( advanced ); + mMaxScaleSpinBox->setVisible( advanced ); + mLimitToScale->setVisible( advanced ); } } diff --git a/src/app/qgssnappingwidget.h b/src/app/qgssnappingwidget.h index d2e1ea0ef1c..489ccc73266 100644 --- a/src/app/qgssnappingwidget.h +++ b/src/app/qgssnappingwidget.h @@ -23,6 +23,7 @@ class QDoubleSpinBox; class QFont; class QToolButton; class QTreeView; +class QCheckBox; class QgsDoubleSpinBox; class QgsFloatingWidget; @@ -100,6 +101,12 @@ class APP_EXPORT QgsSnappingWidget : public QWidget void changeTolerance( double tolerance ); + void changeMinScale( double pMinScale ); + + void changeMaxScale( double pMaxScale ); + + void changeLimitToScale(bool enabled); + void changeUnit( int idx ); void enableTopologicalEditing( bool enabled ); @@ -144,7 +151,13 @@ class APP_EXPORT QgsSnappingWidget : public QWidget QAction *mCentroidAction = nullptr; QAction *mMiddleAction = nullptr; QDoubleSpinBox *mToleranceSpinBox = nullptr; + QAction* mLimitToScale = nullptr; + QDoubleSpinBox *mMinScaleSpinBox = nullptr; + QDoubleSpinBox *mMaxScaleSpinBox = nullptr; QAction *mToleranceAction = nullptr; // hide widget does not work on toolbar, action needed + QAction *mLimitToScaleAction = nullptr; + QAction *mMinScaleAction = nullptr; + QAction *mMaxScaleAction = nullptr; QComboBox *mUnitsComboBox = nullptr; QAction *mUnitAction = nullptr; // hide widget does not work on toolbar, action needed QAction *mTopologicalEditingAction = nullptr; diff --git a/src/core/qgssnappingconfig.cpp b/src/core/qgssnappingconfig.cpp index c22416dbe60..57ffe2de9ea 100644 --- a/src/core/qgssnappingconfig.cpp +++ b/src/core/qgssnappingconfig.cpp @@ -181,7 +181,10 @@ bool QgsSnappingConfig::operator==( const QgsSnappingConfig &other ) const && mTolerance == other.mTolerance && mUnits == other.mUnits && mIntersectionSnapping == other.mIntersectionSnapping - && mIndividualLayerSettings == other.mIndividualLayerSettings; + && mIndividualLayerSettings == other.mIndividualLayerSettings + && mLimitToScale == other.mLimitToScale + && mMinScale == other.mMinScale + && mMaxScale == other.mMaxScale; } void QgsSnappingConfig::reset() @@ -204,6 +207,9 @@ void QgsSnappingConfig::reset() mMode = mode; mType = type; mTolerance = tolerance; + mLimitToScale = false; + mMinScale = 0.0; + mMaxScale = 0.0; // do not allow unit to be "layer" if not in advanced configuration if ( mUnits == QgsTolerance::LayerUnits && mMode != AdvancedConfiguration ) { @@ -225,7 +231,7 @@ void QgsSnappingConfig::reset() QgsVectorLayer *vl = qobject_cast( ml ); if ( vl ) { - mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units, -1.0, -1.0 ) ); + mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units, 0.0, 0.0 ) ); } } } @@ -373,7 +379,10 @@ bool QgsSnappingConfig::operator!=( const QgsSnappingConfig &other ) const || mType != other.mType || mTolerance != other.mTolerance || mUnits != other.mUnits - || mIndividualLayerSettings != other.mIndividualLayerSettings; + || mIndividualLayerSettings != other.mIndividualLayerSettings + || mLimitToScale != other.mLimitToScale + || mMinScale != other.mMinScale + || mMaxScale != other.mMaxScale; } void QgsSnappingConfig::readProject( const QDomDocument &doc ) @@ -437,6 +446,15 @@ void QgsSnappingConfig::readProject( const QDomDocument &doc ) if ( snapSettingsElem.hasAttribute( QStringLiteral( "tolerance" ) ) ) mTolerance = snapSettingsElem.attribute( QStringLiteral( "tolerance" ) ).toDouble(); + if ( snapSettingsElem.hasAttribute( QStringLiteral( "limitToScale" ) ) ) + mLimitToScale = snapSettingsElem.attribute( QStringLiteral( "limitToScale" ) ) == QLatin1String( "1" ); + + if ( snapSettingsElem.hasAttribute( QStringLiteral( "minScale" ) ) ) + mMinScale = snapSettingsElem.attribute( QStringLiteral( "minScale" ) ).toDouble(); + + if ( snapSettingsElem.hasAttribute( QStringLiteral( "maxScale" ) ) ) + mMaxScale = snapSettingsElem.attribute( QStringLiteral( "maxScale" ) ).toDouble(); + if ( snapSettingsElem.hasAttribute( QStringLiteral( "unit" ) ) ) mUnits = ( QgsTolerance::UnitType )snapSettingsElem.attribute( QStringLiteral( "unit" ) ).toInt(); @@ -488,6 +506,9 @@ void QgsSnappingConfig::writeProject( QDomDocument &doc ) snapSettingsElem.setAttribute( QStringLiteral( "tolerance" ), mTolerance ); snapSettingsElem.setAttribute( QStringLiteral( "unit" ), static_cast( mUnits ) ); snapSettingsElem.setAttribute( QStringLiteral( "intersection-snapping" ), QString::number( mIntersectionSnapping ) ); + snapSettingsElem.setAttribute( QStringLiteral( "limitToScale" ), QString::number( mLimitToScale ) ); + snapSettingsElem.setAttribute( QStringLiteral( "minScale" ), mMinScale ); + snapSettingsElem.setAttribute( QStringLiteral( "maxScale" ), mMaxScale ); QDomElement ilsElement = doc.createElement( QStringLiteral( "individual-layer-settings" ) ); QHash::const_iterator layerIt = mIndividualLayerSettings.constBegin(); @@ -524,7 +545,7 @@ bool QgsSnappingConfig::addLayers( const QList &layers ) QgsVectorLayer *vl = qobject_cast( ml ); if ( vl && vl->isSpatial() ) { - mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units, -1.0, -1.0 ) ); + mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units, 0.0, 0.0 ) ); changed = true; } } @@ -599,7 +620,7 @@ void QgsSnappingConfig::readLegacySettings() ) ); - mIndividualLayerSettings.insert( vlayer, IndividualLayerSettings( *enabledIt == QLatin1String( "enabled" ), t, tolIt->toDouble(), static_cast( tolUnitIt->toInt() ), -1.0, -1.0 ) ); + mIndividualLayerSettings.insert( vlayer, IndividualLayerSettings( *enabledIt == QLatin1String( "enabled" ), t, tolIt->toDouble(), static_cast( tolUnitIt->toInt() ), 0.0, 0.0 ) ); } QString snapType = mProject->readEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapType" ), QStringLiteral( "off" ) ); @@ -628,3 +649,37 @@ void QgsSnappingConfig::setProject( QgsProject *project ) reset(); } + +double QgsSnappingConfig::minScale() const +{ + return mMinScale; +} + +void QgsSnappingConfig::setMinScale(double pMinScale) +{ + mMinScale = pMinScale; +} + +double QgsSnappingConfig::maxScale() const +{ + return mMaxScale; +} + +void QgsSnappingConfig::setMaxScale(double pMaxScale) +{ + mMaxScale = pMaxScale; +} + +bool QgsSnappingConfig::limitToScale() const +{ + return mLimitToScale; +} + +void QgsSnappingConfig::setLimitToScale( bool pLimitSnapping ) +{ + mLimitToScale = pLimitSnapping; +} + + + + diff --git a/src/core/qgssnappingconfig.h b/src/core/qgssnappingconfig.h index f6c337d4607..6c7edea6f1d 100644 --- a/src/core/qgssnappingconfig.h +++ b/src/core/qgssnappingconfig.h @@ -265,6 +265,24 @@ class CORE_EXPORT QgsSnappingConfig //! Sets the tolerance void setTolerance( double tolerance ); + //! Returns the min scale + double minScale() const; + + //! Sets the min scale + void setMinScale( double pMinScale ); + + //! Returns the max scale + double maxScale() const; + + //! Set the max scale + void setMaxScale( double pMaxScale ); + + //! Returns limit to scale + bool limitToScale() const; + + //! Set limit to scale + void setLimitToScale( bool pLimitSnapping ); + //! Returns the type of units QgsTolerance::UnitType units() const; @@ -395,6 +413,9 @@ class CORE_EXPORT QgsSnappingConfig SnappingMode mMode = ActiveLayer; SnappingTypeFlag mType = VertexFlag; double mTolerance = 0.0; + bool mLimitToScale = false; + double mMinScale = 0.0; + double mMaxScale = 0.0; QgsTolerance::UnitType mUnits = QgsTolerance::ProjectUnits; bool mIntersectionSnapping = false; diff --git a/src/core/qgssnappingutils.cpp b/src/core/qgssnappingutils.cpp index 84dbad0c740..f6bd8342c77 100644 --- a/src/core/qgssnappingutils.cpp +++ b/src/core/qgssnappingutils.cpp @@ -295,14 +295,20 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPointXY &pointMap, QList layers; QList filteredConfigs; + bool inRangeGlobal = ( mSnappingConfig.minScale() <= 0.0 || mMapSettings.scale() >= mSnappingConfig.minScale() ) + && ( mSnappingConfig.maxScale() <= 0.0 || mMapSettings.scale() <= mSnappingConfig.maxScale() ); + for ( LayerConfigIterator it = mLayers.begin(); it != mLayers.end(); ++it ) { const LayerConfig &layerConfig = *it; QgsSnappingConfig::IndividualLayerSettings layerSettings = mSnappingConfig.individualLayerSettings( layerConfig.layer ); - //Add the layers only if scale is in specified range. Value < 0.0 disable the limit. - bool inRange = ( layerSettings.minScale() < 0.0 || mMapSettings.scale() >= layerSettings.minScale() ) && ( layerSettings.maxScale() < 0.0 || mMapSettings.scale() <= layerSettings.maxScale() ); - if ( inRange ) + //Default value for layer config means it is not set (appears NULL) + bool layerSpecificRange = layerSettings.minScale() > 0.0 || layerSettings.maxScale() > 0.0; + bool inRangeLayer = ( layerSettings.minScale() < 0.0 || mMapSettings.scale() >= layerSettings.minScale() ) && ( layerSettings.maxScale() < 0.0 || mMapSettings.scale() <= layerSettings.maxScale() ); + + //If no per layer config is set use the global one otherwise use the layer config if it is set + if ( ( !layerSpecificRange && inRangeGlobal ) || ( layerSpecificRange && inRangeLayer) ) { double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit ); layers << qMakePair( layerConfig.layer, _areaOfInterest( pointMap, tolerance ) );