Skip to content

Commit 334d885

Browse files
committed
Snapping tolerance: Fix "map units" vs "layer units" dilemma (fixes #11634)
Until now "map units" in snapping config dialog actually referred to layer units. From now on, "map units" refer to project's CRS units. Where appropriate, it is possible to choose "layer units" that refer to layer CRS units. Projects from older versions of QGIS should be handled seamlessly (tolerances in layer units will be still handled in layer units, not project units). In API, QgsTolerance::MapUnits is deprecated as it is unclear to what units it refers to (should be ProjectUnits, but actually it is LayerUnits)
1 parent 7e0f646 commit 334d885

File tree

8 files changed

+93
-38
lines changed

8 files changed

+93
-38
lines changed

python/core/qgstolerance.sip

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ class QgsTolerance
66
%End
77

88
public:
9-
/**Type of unit of tolerance value from settings*/
9+
/** Type of unit of tolerance value from settings.
10+
* MapUnits is slightly confusing, because it actually refers to layer units (historically).
11+
* For map (project) units, use ProjectUnits. Try to avoid using MapUnits value and use LayerUnits instead. */
1012
enum UnitType
1113
{
12-
/**Map unit value*/
14+
/**Layer unit value. @note deprecated: use LayerUnits */
1315
MapUnits,
16+
/**Layer unit value */
17+
LayerUnits,
1418
/**Pixels unit of tolerance*/
15-
Pixels
19+
Pixels,
20+
/**Map (project) units. Added in 2.8 */
21+
ProjectUnits
1622
};
1723

1824
/**
@@ -56,12 +62,13 @@ class QgsTolerance
5662
/**
5763
* Static function to translate tolerance value into map units
5864
* @param tolerance tolerance value to be translated
65+
* @param layer source layer necessary in case tolerance is in layer units
5966
* @param mapSettings settings of the map
6067
* @param units type of units to be translated
6168
* @return value of tolerance in map units
6269
* @note added in 2.8
6370
*/
64-
static double toleranceInMapUnits( double tolerance, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units );
71+
static double toleranceInProjectUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units );
6572

6673
/**
6774
* Static function to translate tolerance value into layer units
@@ -71,7 +78,7 @@ class QgsTolerance
7178
* @param units type of units to be translated
7279
* @return value of tolerance in layer units
7380
*/
74-
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, UnitType units = MapUnits );
81+
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, UnitType units = LayerUnits );
7582

7683
/**
7784
* Static function to translate tolerance value into layer units
@@ -82,5 +89,5 @@ class QgsTolerance
8289
* @return value of tolerance in layer units
8390
*/
8491
//! @deprecated since 2.4 - use the override with QgsMapSettings
85-
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, QgsMapRenderer* renderer, UnitType units = MapUnits ) /Deprecated/;
92+
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, QgsMapRenderer* renderer, UnitType units = LayerUnits ) /Deprecated/;
8693
};

src/app/qgsoptions.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) :
801801
mDefaultSnapModeComboBox->setCurrentIndex( mDefaultSnapModeComboBox->findData( defaultSnapString ) );
802802
mDefaultSnappingToleranceSpinBox->setValue( settings.value( "/qgis/digitizing/default_snapping_tolerance", 0 ).toDouble() );
803803
mSearchRadiusVertexEditSpinBox->setValue( settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble() );
804-
if ( settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", 0 ).toInt() == QgsTolerance::MapUnits )
804+
int defSnapUnits = settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", QgsTolerance::ProjectUnits ).toInt();
805+
if ( defSnapUnits == QgsTolerance::ProjectUnits || defSnapUnits == QgsTolerance::LayerUnits )
805806
{
806807
index = mDefaultSnappingToleranceComboBox->findText( tr( "map units" ) );
807808
}
@@ -810,7 +811,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl ) :
810811
index = mDefaultSnappingToleranceComboBox->findText( tr( "pixels" ) );
811812
}
812813
mDefaultSnappingToleranceComboBox->setCurrentIndex( index );
813-
if ( settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt() == QgsTolerance::MapUnits )
814+
int defRadiusUnits = settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt();
815+
if ( defRadiusUnits == QgsTolerance::ProjectUnits || defSnapUnits == QgsTolerance::LayerUnits )
814816
{
815817
index = mSearchRadiusVertexEditComboBox->findText( tr( "map units" ) );
816818
}
@@ -1272,9 +1274,9 @@ void QgsOptions::saveOptions()
12721274
settings.setValue( "/qgis/digitizing/default_snapping_tolerance", mDefaultSnappingToleranceSpinBox->value() );
12731275
settings.setValue( "/qgis/digitizing/search_radius_vertex_edit", mSearchRadiusVertexEditSpinBox->value() );
12741276
settings.setValue( "/qgis/digitizing/default_snapping_tolerance_unit",
1275-
( mDefaultSnappingToleranceComboBox->currentIndex() == 0 ? QgsTolerance::MapUnits : QgsTolerance::Pixels ) );
1277+
( mDefaultSnappingToleranceComboBox->currentIndex() == 0 ? QgsTolerance::ProjectUnits : QgsTolerance::Pixels ) );
12761278
settings.setValue( "/qgis/digitizing/search_radius_vertex_edit_unit",
1277-
( mSearchRadiusVertexEditComboBox->currentIndex() == 0 ? QgsTolerance::MapUnits : QgsTolerance::Pixels ) );
1279+
( mSearchRadiusVertexEditComboBox->currentIndex() == 0 ? QgsTolerance::ProjectUnits : QgsTolerance::Pixels ) );
12781280

12791281
settings.setValue( "/qgis/digitizing/marker_only_for_selected", mMarkersOnlyForSelectedCheckBox->isChecked() );
12801282

src/app/qgssnappingdialog.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,9 @@ void QgsSnappingDialog::reload()
137137
tolerance = QgsProject::instance()->readDoubleEntry( "Digitizing", "/DefaultSnapTolerance", tolerance );
138138
mDefaultSnappingToleranceSpinBox->setValue( tolerance );
139139

140-
int unit = settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", 0 ).toInt();
140+
int unit = settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", QgsTolerance::ProjectUnits ).toInt();
141141
unit = QgsProject::instance()->readNumEntry( "Digitizing", "/DefaultSnapToleranceUnit", unit );
142-
mDefaultSnappingToleranceComboBox->setCurrentIndex( unit );
142+
mDefaultSnappingToleranceComboBox->setCurrentIndex( unit == QgsTolerance::Pixels ? 1 : 0 );
143143

144144
mLayerTreeWidget->clear();
145145

@@ -182,7 +182,7 @@ void QgsSnappingDialog::initNewProject()
182182
QgsProject::instance()->writeEntry( "Digitizing", "/DefaultSnapType", snapType );
183183
double tolerance = settings.value( "/qgis/digitizing/default_snapping_tolerance", 0 ).toDouble();
184184
QgsProject::instance()->writeEntry( "Digitizing", "/DefaultSnapTolerance", tolerance );
185-
int unit = settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", 0 ).toInt();
185+
int unit = settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", QgsTolerance::ProjectUnits ).toInt();
186186
QgsProject::instance()->writeEntry( "Digitizing", "/DefaultSnapToleranceUnit", unit );
187187

188188
reload();
@@ -215,7 +215,7 @@ void QgsSnappingDialog::apply()
215215
QString snapType = mDefaultSnapToComboBox->itemData( mDefaultSnapToComboBox->currentIndex() ).toString();
216216
QgsProject::instance()->writeEntry( "Digitizing", "/DefaultSnapType", snapType );
217217
QgsProject::instance()->writeEntry( "Digitizing", "/DefaultSnapTolerance", mDefaultSnappingToleranceSpinBox->value() );
218-
QgsProject::instance()->writeEntry( "Digitizing", "/DefaultSnapToleranceUnit", mDefaultSnappingToleranceComboBox->currentIndex() );
218+
QgsProject::instance()->writeEntry( "Digitizing", "/DefaultSnapToleranceUnit", mDefaultSnappingToleranceComboBox->currentIndex() == 1 ? QgsTolerance::Pixels : QgsTolerance::ProjectUnits );
219219

220220

221221
QStringList layerIdList;
@@ -314,7 +314,7 @@ void QgsSnappingDialog::addLayer( QgsMapLayer *theMapLayer )
314314
QSettings myQsettings;
315315
bool myDockFlag = myQsettings.value( "/qgis/dockSnapping", false ).toBool();
316316
double defaultSnappingTolerance = myQsettings.value( "/qgis/digitizing/default_snapping_tolerance", 0 ).toDouble();
317-
int defaultSnappingUnit = myQsettings.value( "/qgis/digitizing/default_snapping_tolerance_unit", 0 ).toInt();
317+
int defaultSnappingUnit = myQsettings.value( "/qgis/digitizing/default_snapping_tolerance_unit", QgsTolerance::ProjectUnits ).toInt();
318318
QString defaultSnappingString = myQsettings.value( "/qgis/digitizing/default_snap_mode", "to vertex" ).toString();
319319

320320
int defaultSnappingStringIdx = 0;
@@ -366,8 +366,9 @@ void QgsSnappingDialog::addLayer( QgsMapLayer *theMapLayer )
366366

367367
//snap to vertex/ snap to segment
368368
QComboBox *cbxUnits = new QComboBox( mLayerTreeWidget );
369-
cbxUnits->insertItem( 0, tr( "map units" ) );
369+
cbxUnits->insertItem( 0, tr( "layer units" ) );
370370
cbxUnits->insertItem( 1, tr( "pixels" ) );
371+
cbxUnits->insertItem( 2, tr( "map units" ) );
371372
cbxUnits->setCurrentIndex( defaultSnappingUnit );
372373
mLayerTreeWidget->setItemWidget( item, 4, cbxUnits );
373374

src/core/qgsproject.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,7 +1931,7 @@ void QgsProject::setSnapSettingsForLayer( const QString& layerId, bool enabled,
19311931
snapTypeList.append( typeString );
19321932

19331933
//units
1934-
toleranceUnitList.append( unit == QgsTolerance::Pixels ? "1" : "0" );
1934+
toleranceUnitList.append( QString::number( unit ) );
19351935

19361936
//tolerance
19371937
toleranceList.append( QString::number( tolerance ) );
@@ -1993,9 +1993,13 @@ bool QgsProject::snapSettingsForLayer( const QString& layerId, bool& enabled, Qg
19931993
{
19941994
units = QgsTolerance::Pixels;
19951995
}
1996+
else if ( toleranceUnitList.at( idx ) == "2" )
1997+
{
1998+
units = QgsTolerance::ProjectUnits;
1999+
}
19962000
else
19972001
{
1998-
units = QgsTolerance::MapUnits;
2002+
units = QgsTolerance::LayerUnits;
19992003
}
20002004

20012005
//tolerance

src/core/qgssnappingutils.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
216216
prepareIndex( QList<QgsVectorLayer*>() << mCurrentLayer );
217217

218218
// data from project
219-
double tolerance = QgsTolerance::toleranceInMapUnits( mDefaultTolerance, mMapSettings, mDefaultUnit );
219+
double tolerance = QgsTolerance::toleranceInProjectUnits( mDefaultTolerance, mCurrentLayer, mMapSettings, mDefaultUnit );
220220
int type = mDefaultType;
221221

222222
// use ad-hoc locator
@@ -249,7 +249,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
249249

250250
foreach ( const LayerConfig& layerConfig, mLayers )
251251
{
252-
double tolerance = QgsTolerance::toleranceInMapUnits( layerConfig.tolerance, mMapSettings, layerConfig.unit );
252+
double tolerance = QgsTolerance::toleranceInProjectUnits( layerConfig.tolerance, layerConfig.layer, mMapSettings, layerConfig.unit );
253253
if ( QgsPointLocator* loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
254254
{
255255
_updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter );
@@ -270,7 +270,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
270270
else if ( mSnapToMapMode == SnapAllLayers )
271271
{
272272
// data from project
273-
double tolerance = QgsTolerance::toleranceInMapUnits( mDefaultTolerance, mMapSettings, mDefaultUnit );
273+
double tolerance = QgsTolerance::toleranceInProjectUnits( mDefaultTolerance, 0, mMapSettings, mDefaultUnit );
274274
int type = mDefaultType;
275275

276276
QList<QgsVectorLayer*> layers;
@@ -360,6 +360,10 @@ void QgsSnappingUtils::setMapSettings( const QgsMapSettings& settings )
360360

361361
void QgsSnappingUtils::setDefaultSettings( int type, double tolerance, QgsTolerance::UnitType unit )
362362
{
363+
// force map units - can't use layer units for just any layer
364+
if ( unit == QgsTolerance::LayerUnits )
365+
unit = QgsTolerance::ProjectUnits;
366+
363367
mDefaultType = type;
364368
mDefaultTolerance = tolerance;
365369
mDefaultUnit = unit;
@@ -394,7 +398,7 @@ void QgsSnappingUtils::readConfigFromProject()
394398
else if ( snapType == "to vertex" )
395399
type = QgsPointLocator::Vertex;
396400
double tolerance = QgsProject::instance()->readDoubleEntry( "Digitizing", "/DefaultSnapTolerance", 0 );
397-
QgsTolerance::UnitType unit = ( QgsTolerance::UnitType ) QgsProject::instance()->readNumEntry( "Digitizing", "/DefaultSnapToleranceUnit", 0 );
401+
QgsTolerance::UnitType unit = ( QgsTolerance::UnitType ) QgsProject::instance()->readNumEntry( "Digitizing", "/DefaultSnapToleranceUnit", QgsTolerance::ProjectUnits );
398402
setDefaultSettings( type, tolerance, unit );
399403

400404
//snapping on intersection on?

src/core/qgstolerance.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,52 @@
1919
#include <cmath>
2020

2121

22-
double QgsTolerance::toleranceInMapUnits( double tolerance, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units )
22+
// return ratio [mu/lu] between map units and layer units
23+
// this is of course only an approximation
24+
double _ratioMU2LU( const QgsMapSettings& mapSettings, QgsMapLayer* layer )
25+
{
26+
double distMU = mapSettings.mapUnitsPerPixel();
27+
QgsPoint ptMapCenterMU = mapSettings.visibleExtent().center();
28+
QgsPoint ptMapCenterRightMU( ptMapCenterMU.x() + distMU, ptMapCenterMU.y() );
29+
QgsPoint ptMapCenterLU = mapSettings.mapToLayerCoordinates( layer, ptMapCenterMU );
30+
QgsPoint ptMapCenterRightLU = mapSettings.mapToLayerCoordinates( layer, ptMapCenterRightMU );
31+
double distLU = sqrt( ptMapCenterLU.sqrDist( ptMapCenterRightLU ) );
32+
double ratio = distMU / distLU;
33+
return ratio;
34+
}
35+
36+
double QgsTolerance::toleranceInProjectUnits(double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units )
2337
{
2438
// converts to map units
25-
if ( units == MapUnits )
39+
if ( units == ProjectUnits )
2640
return tolerance;
27-
else
41+
else if ( units == Pixels )
2842
return tolerance * mapSettings.mapUnitsPerPixel();
43+
else // units == LayerUnits
44+
{
45+
// [mu] = [lu] * [mu/lu]
46+
return tolerance * _ratioMU2LU( mapSettings, layer );
47+
}
2948
}
3049

50+
3151
double QgsTolerance::toleranceInMapUnits( double tolerance, QgsMapLayer *layer, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units )
3252
{
3353
// converts to layer units
34-
if ( units == MapUnits )
54+
if ( units == LayerUnits )
3555
{
3656
return tolerance;
3757
}
38-
double mapUnitsPerPixel = computeMapUnitPerPixel( layer, mapSettings );
39-
return tolerance * mapUnitsPerPixel;
58+
else if ( units == Pixels )
59+
{
60+
double layerUnitsPerPixel = computeMapUnitPerPixel( layer, mapSettings );
61+
return tolerance * layerUnitsPerPixel;
62+
}
63+
else // ProjectUnits
64+
{
65+
// [lu] = [mu] / [mu/lu]
66+
return tolerance / _ratioMU2LU( mapSettings, layer );
67+
}
4068
}
4169

4270
double QgsTolerance::toleranceInMapUnits( double tolerance, QgsMapLayer* layer, QgsMapRenderer* renderer, UnitType units )
@@ -49,7 +77,9 @@ double QgsTolerance::vertexSearchRadius( const QgsMapSettings& mapSettings )
4977
QSettings settings;
5078
double tolerance = settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble();
5179
UnitType units = ( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt();
52-
return toleranceInMapUnits( tolerance, mapSettings, units );
80+
if ( units == LayerUnits )
81+
units = ProjectUnits;
82+
return toleranceInProjectUnits( tolerance, 0, mapSettings, units );
5383
}
5484

5585
double QgsTolerance::vertexSearchRadius( QgsMapLayer *layer, const QgsMapSettings &mapSettings )
@@ -69,7 +99,7 @@ double QgsTolerance::defaultTolerance( QgsMapLayer *layer, const QgsMapSettings&
6999
{
70100
QSettings settings;
71101
double tolerance = settings.value( "/qgis/digitizing/default_snapping_tolerance", 0 ).toDouble();
72-
UnitType units = ( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", 0 ).toInt();
102+
UnitType units = ( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", ProjectUnits ).toInt();
73103
return toleranceInMapUnits( tolerance, layer, mapSettings, units );
74104
}
75105

src/core/qgstolerance.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,19 @@ class CORE_EXPORT QgsTolerance
2727
{
2828

2929
public:
30-
/**Type of unit of tolerance value from settings*/
30+
/** Type of unit of tolerance value from settings.
31+
* MapUnits is slightly confusing, because it actually refers to layer units (historically).
32+
* For map (project) units, use ProjectUnits. Try to avoid using MapUnits value and use LayerUnits instead. */
3133
enum UnitType
3234
{
33-
/**Map unit value*/
35+
/**Layer unit value. @note deprecated: use LayerUnits */
3436
MapUnits,
37+
/**Layer unit value */
38+
LayerUnits = MapUnits,
3539
/**Pixels unit of tolerance*/
36-
Pixels
40+
Pixels,
41+
/**Map (project) units. Added in 2.8 */
42+
ProjectUnits
3743
};
3844

3945
/**
@@ -77,12 +83,13 @@ class CORE_EXPORT QgsTolerance
7783
/**
7884
* Static function to translate tolerance value into map units
7985
* @param tolerance tolerance value to be translated
86+
* @param layer source layer necessary in case tolerance is in layer units
8087
* @param mapSettings settings of the map
8188
* @param units type of units to be translated
8289
* @return value of tolerance in map units
8390
* @note added in 2.8
8491
*/
85-
static double toleranceInMapUnits( double tolerance, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units );
92+
static double toleranceInProjectUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, QgsTolerance::UnitType units );
8693

8794
/**
8895
* Static function to translate tolerance value into layer units
@@ -92,7 +99,7 @@ class CORE_EXPORT QgsTolerance
9299
* @param units type of units to be translated
93100
* @return value of tolerance in layer units
94101
*/
95-
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, UnitType units = MapUnits );
102+
static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, const QgsMapSettings& mapSettings, UnitType units = LayerUnits );
96103

97104
/**
98105
* Static function to translate tolerance value into layer units
@@ -103,7 +110,7 @@ class CORE_EXPORT QgsTolerance
103110
* @return value of tolerance in layer units
104111
*/
105112
//! @deprecated since 2.4 - use the override with QgsMapSettings
106-
Q_DECL_DEPRECATED static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, QgsMapRenderer* renderer, UnitType units = MapUnits );
113+
Q_DECL_DEPRECATED static double toleranceInMapUnits( double tolerance, QgsMapLayer* layer, QgsMapRenderer* renderer, UnitType units = LayerUnits );
107114

108115
private:
109116
static double computeMapUnitPerPixel( QgsMapLayer* layer, const QgsMapSettings& mapSettings );

src/gui/qgsmapcanvassnapper.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ int QgsMapCanvasSnapper::snapToCurrentLayer( const QPoint& p, QList<QgsSnappingR
9292
QgsSnapper::SnapLayer snapLayer;
9393
snapLayer.mLayer = vlayer;
9494
snapLayer.mSnapTo = snap_to;
95-
snapLayer.mUnitType = QgsTolerance::MapUnits;
95+
snapLayer.mUnitType = QgsTolerance::LayerUnits;
9696

9797
if ( snappingTol < 0 )
9898
{
@@ -250,7 +250,7 @@ int QgsMapCanvasSnapper::snapToBackgroundLayers( const QgsPoint& point, QList<Qg
250250

251251
//default snapping tolerance (returned in map units)
252252
snapLayer.mTolerance = QgsTolerance::defaultTolerance( currentVectorLayer, mMapCanvas->mapSettings() );
253-
snapLayer.mUnitType = QgsTolerance::MapUnits;
253+
snapLayer.mUnitType = QgsTolerance::LayerUnits;
254254

255255
snapLayers.append( snapLayer );
256256
}

0 commit comments

Comments
 (0)