QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgssnappingconfig.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprojectsnappingsettings.cpp - QgsProjectSnappingSettings
3 
4  ---------------------
5  begin : 29.8.2016
6  copyright : (C) 2016 by Denis Rouzaud
7  email : denis.rouzaud@gmail.com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 #include "qgssnappingconfig.h"
17 
18 #include <QDomElement>
19 #include <QHeaderView>
20 
21 #include "qgssettings.h"
22 #include "qgslogger.h"
23 #include "qgsvectorlayer.h"
24 #include "qgsproject.h"
25 #include "qgsapplication.h"
26 
27 
29  : mValid( true )
30  , mEnabled( enabled )
31  , mType( type )
32  , mTolerance( tolerance )
33  , mUnits( units )
34 {}
35 
37 {
38  return mValid;
39 }
40 
42 {
43  return mEnabled;
44 }
45 
47 {
48  mEnabled = enabled;
49 }
50 
52 {
53  return mType;
54 }
55 
57 {
58  mType = type;
59 }
60 
62 {
63  return mTolerance;
64 }
65 
67 {
68  mTolerance = tolerance;
69 }
70 
72 {
73  return mUnits;
74 }
75 
77 {
78  mUnits = units;
79 }
80 
82 {
83  return mValid != other.mValid
84  || mEnabled != other.mEnabled
85  || mType != other.mType
86  || mTolerance != other.mTolerance
87  || mUnits != other.mUnits;
88 }
89 
91 {
92  return mValid == other.mValid
93  && mEnabled == other.mEnabled
94  && mType == other.mType
95  && mTolerance == other.mTolerance
96  && mUnits == other.mUnits;
97 }
98 
99 
101  : mProject( project )
102 {
103  if ( project )
104  reset();
105 }
106 
108 {
109  return mEnabled == other.mEnabled
110  && mMode == other.mMode
111  && mType == other.mType
112  && mTolerance == other.mTolerance
113  && mUnits == other.mUnits
114  && mIntersectionSnapping == other.mIntersectionSnapping
115  && mIndividualLayerSettings == other.mIndividualLayerSettings;
116 }
117 
119 {
120  // get defaults values. They are both used for standard and advanced configuration (per layer)
121  bool enabled = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snap_enabled" ), false ).toBool();
122  SnappingMode mode = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snap_mode" ), AllLayers );
123  if ( mode == 0 )
124  {
125  // backward compatibility with QGIS 2.x
126  // could be removed in 3.4+
127  mode = AllLayers;
128  }
129  SnappingType type = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snap_type" ), Vertex );
130  double tolerance = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance" ), Qgis::DEFAULT_SNAP_TOLERANCE ).toDouble();
131  QgsTolerance::UnitType units = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance_unit" ), Qgis::DEFAULT_SNAP_UNITS );
132 
133  // assign main (standard) config
134  mEnabled = enabled;
135  mMode = mode;
136  mType = type;
137  mTolerance = tolerance;
138  // do not allow unit to be "layer" if not in advanced configuration
139  if ( mUnits == QgsTolerance::LayerUnits && mMode != AdvancedConfiguration )
140  {
142  }
143  else
144  {
145  mUnits = units;
146  }
147  mIntersectionSnapping = false;
148 
149  // set advanced config
150  mIndividualLayerSettings = QHash<QgsVectorLayer *, IndividualLayerSettings>();
151  const auto constMapLayers = mProject->mapLayers();
152  for ( QgsMapLayer *ml : constMapLayers )
153  {
154  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
155  if ( vl )
156  {
157  mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
158  }
159  }
160 }
161 
163 {
164  return mEnabled;
165 }
166 
168 {
169  if ( mEnabled == enabled )
170  {
171  return;
172  }
173  mEnabled = enabled;
174 }
175 
177 {
178  return mMode;
179 }
180 
182 {
183  if ( mMode == mode )
184  {
185  return;
186  }
187  mMode = mode;
188 }
189 
191 {
192  return mType;
193 }
194 
196 {
197  if ( mType == type )
198  {
199  return;
200  }
201  mType = type;
202 }
203 
205 {
206  return mTolerance;
207 }
208 
210 {
211  if ( mTolerance == tolerance )
212  {
213  return;
214  }
215  mTolerance = tolerance;
216 }
217 
219 {
220  return mUnits;
221 }
222 
224 {
225  if ( mUnits == units )
226  {
227  return;
228  }
229  mUnits = units;
230 }
231 
233 {
234  return mIntersectionSnapping;
235 }
236 
238 {
239  mIntersectionSnapping = enabled;
240 }
241 
242 QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> QgsSnappingConfig::individualLayerSettings() const
243 {
244  return mIndividualLayerSettings;
245 }
246 
248 {
249  if ( vl && mIndividualLayerSettings.contains( vl ) )
250  {
251  return mIndividualLayerSettings.value( vl );
252  }
253  else
254  {
255  // return invalid settings
256  return IndividualLayerSettings();
257  }
258 }
259 
261 {
262  if ( !vl || !vl->isSpatial() || mIndividualLayerSettings.value( vl ) == individualLayerSettings )
263  {
264  return;
265  }
266  mIndividualLayerSettings.insert( vl, individualLayerSettings );
267 }
268 
270 {
271  return mEnabled != other.mEnabled
272  || mMode != other.mMode
273  || mType != other.mType
274  || mTolerance != other.mTolerance
275  || mUnits != other.mUnits
276  || mIndividualLayerSettings != other.mIndividualLayerSettings;
277 }
278 
279 void QgsSnappingConfig::readProject( const QDomDocument &doc )
280 {
281  QDomElement snapSettingsElem = doc.firstChildElement( QStringLiteral( "qgis" ) ).firstChildElement( QStringLiteral( "snapping-settings" ) );
282  if ( snapSettingsElem.isNull() )
283  {
284  readLegacySettings();
285  return;
286  }
287 
288  if ( snapSettingsElem.hasAttribute( QStringLiteral( "enabled" ) ) )
289  mEnabled = snapSettingsElem.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "1" );
290 
291  if ( snapSettingsElem.hasAttribute( QStringLiteral( "mode" ) ) )
292  mMode = ( SnappingMode )snapSettingsElem.attribute( QStringLiteral( "mode" ) ).toInt();
293 
294  if ( snapSettingsElem.hasAttribute( QStringLiteral( "type" ) ) )
295  mType = ( SnappingType )snapSettingsElem.attribute( QStringLiteral( "type" ) ).toInt();
296 
297  if ( snapSettingsElem.hasAttribute( QStringLiteral( "tolerance" ) ) )
298  mTolerance = snapSettingsElem.attribute( QStringLiteral( "tolerance" ) ).toDouble();
299 
300  if ( snapSettingsElem.hasAttribute( QStringLiteral( "unit" ) ) )
301  mUnits = ( QgsTolerance::UnitType )snapSettingsElem.attribute( QStringLiteral( "unit" ) ).toInt();
302 
303  if ( snapSettingsElem.hasAttribute( QStringLiteral( "intersection-snapping" ) ) )
304  mIntersectionSnapping = snapSettingsElem.attribute( QStringLiteral( "intersection-snapping" ) ) == QLatin1String( "1" );
305 
306  // do not clear the settings as they must be automatically synchronized with current layers
307  QDomNodeList nodes = snapSettingsElem.elementsByTagName( QStringLiteral( "individual-layer-settings" ) );
308  if ( nodes.count() )
309  {
310  QDomNode node = nodes.item( 0 );
311  QDomNodeList settingNodes = node.childNodes();
312  int layerCount = settingNodes.count();
313  for ( int i = 0; i < layerCount; ++i )
314  {
315  QDomElement settingElement = settingNodes.at( i ).toElement();
316  if ( settingElement.tagName() != QLatin1String( "layer-setting" ) )
317  {
318  QgsLogger::warning( QApplication::translate( "QgsProjectSnappingSettings", "Cannot read individual settings. Unexpected tag '%1'" ).arg( settingElement.tagName() ) );
319  continue;
320  }
321 
322  QString layerId = settingElement.attribute( QStringLiteral( "id" ) );
323  bool enabled = settingElement.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "1" );
324  SnappingType type = ( SnappingType )settingElement.attribute( QStringLiteral( "type" ) ).toInt();
325  double tolerance = settingElement.attribute( QStringLiteral( "tolerance" ) ).toDouble();
326  QgsTolerance::UnitType units = ( QgsTolerance::UnitType )settingElement.attribute( QStringLiteral( "units" ) ).toInt();
327 
328  QgsMapLayer *ml = mProject->mapLayer( layerId );
329  if ( !ml || ml->type() != QgsMapLayerType::VectorLayer )
330  continue;
331 
332  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
333 
334  IndividualLayerSettings setting = IndividualLayerSettings( enabled, type, tolerance, units );
335  mIndividualLayerSettings.insert( vl, setting );
336  }
337  }
338 }
339 
340 void QgsSnappingConfig::writeProject( QDomDocument &doc )
341 {
342  QDomElement snapSettingsElem = doc.createElement( QStringLiteral( "snapping-settings" ) );
343  snapSettingsElem.setAttribute( QStringLiteral( "enabled" ), QString::number( mEnabled ) );
344  snapSettingsElem.setAttribute( QStringLiteral( "mode" ), static_cast<int>( mMode ) );
345  snapSettingsElem.setAttribute( QStringLiteral( "type" ), static_cast<int>( mType ) );
346  snapSettingsElem.setAttribute( QStringLiteral( "tolerance" ), mTolerance );
347  snapSettingsElem.setAttribute( QStringLiteral( "unit" ), static_cast<int>( mUnits ) );
348  snapSettingsElem.setAttribute( QStringLiteral( "intersection-snapping" ), QString::number( mIntersectionSnapping ) );
349 
350  QDomElement ilsElement = doc.createElement( QStringLiteral( "individual-layer-settings" ) );
351  QHash<QgsVectorLayer *, IndividualLayerSettings>::const_iterator layerIt = mIndividualLayerSettings.constBegin();
352  for ( ; layerIt != mIndividualLayerSettings.constEnd(); ++layerIt )
353  {
354  const IndividualLayerSettings &setting = layerIt.value();
355 
356  QDomElement layerElement = doc.createElement( QStringLiteral( "layer-setting" ) );
357  layerElement.setAttribute( QStringLiteral( "id" ), layerIt.key()->id() );
358  layerElement.setAttribute( QStringLiteral( "enabled" ), QString::number( setting.enabled() ) );
359  layerElement.setAttribute( QStringLiteral( "type" ), static_cast<int>( setting.type() ) );
360  layerElement.setAttribute( QStringLiteral( "tolerance" ), setting.tolerance() );
361  layerElement.setAttribute( QStringLiteral( "units" ), static_cast<int>( setting.units() ) );
362  ilsElement.appendChild( layerElement );
363  }
364  snapSettingsElem.appendChild( ilsElement );
365 
366  doc.firstChildElement( QStringLiteral( "qgis" ) ).appendChild( snapSettingsElem );
367 }
368 
369 bool QgsSnappingConfig::addLayers( const QList<QgsMapLayer *> &layers )
370 {
371  bool changed = false;
372  bool enabled = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snap_enabled" ), true ).toBool();
373  SnappingType type = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snap_type" ), Vertex );
374  double tolerance = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance" ), Qgis::DEFAULT_SNAP_TOLERANCE ).toDouble();
375  QgsTolerance::UnitType units = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance_unit" ), Qgis::DEFAULT_SNAP_UNITS );
376 
377  const auto constLayers = layers;
378  for ( QgsMapLayer *ml : constLayers )
379  {
380  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
381  if ( vl && vl->isSpatial() )
382  {
383  mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
384  changed = true;
385  }
386  }
387  return changed;
388 }
389 
390 bool QgsSnappingConfig::removeLayers( const QList<QgsMapLayer *> &layers )
391 {
392  bool changed = false;
393  const auto constLayers = layers;
394  for ( QgsMapLayer *ml : constLayers )
395  {
396  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
397  if ( vl )
398  {
399  mIndividualLayerSettings.remove( vl );
400  changed = true;
401  }
402  }
403  return changed;
404 }
405 
406 void QgsSnappingConfig::readLegacySettings()
407 {
408  //
409  mMode = ActiveLayer;
410 
411  QString snapMode = mProject->readEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/SnappingMode" ) );
412 
413  mTolerance = mProject->readDoubleEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapTolerance" ), 0 );
414  mUnits = static_cast< QgsTolerance::UnitType >( mProject->readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapToleranceUnit" ), QgsTolerance::ProjectUnits ) );
415 
416  mIntersectionSnapping = mProject->readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/IntersectionSnapping" ), 0 );
417 
418  //read snapping settings from project
419  QStringList layerIdList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingList" ), QStringList() );
420  QStringList enabledList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingEnabledList" ), QStringList() );
421  QStringList toleranceList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingToleranceList" ), QStringList() );
422  QStringList toleranceUnitList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingToleranceUnitList" ), QStringList() );
423  QStringList snapToList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnapToList" ), QStringList() );
424 
425  // lists must have the same size, otherwise something is wrong
426  if ( layerIdList.size() != enabledList.size() ||
427  layerIdList.size() != toleranceList.size() ||
428  layerIdList.size() != toleranceUnitList.size() ||
429  layerIdList.size() != snapToList.size() )
430  return;
431 
432  // Use snapping information from the project
433  if ( snapMode == QLatin1String( "current_layer" ) )
434  mMode = ActiveLayer;
435  else if ( snapMode == QLatin1String( "all_layers" ) )
436  mMode = AllLayers;
437  else // either "advanced" or empty (for background compatibility)
438  mMode = AdvancedConfiguration;
439 
440  // load layers, tolerances, snap type
441  QStringList::const_iterator layerIt( layerIdList.constBegin() );
442  QStringList::const_iterator tolIt( toleranceList.constBegin() );
443  QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
444  QStringList::const_iterator snapIt( snapToList.constBegin() );
445  QStringList::const_iterator enabledIt( enabledList.constBegin() );
446  for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
447  {
448  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mProject->mapLayer( *layerIt ) );
449  if ( !vlayer || !vlayer->isSpatial() )
450  continue;
451 
452  SnappingType t( *snapIt == QLatin1String( "to_vertex" ) ? Vertex :
453  ( *snapIt == QLatin1String( "to_segment" ) ? Segment :
455  )
456  );
457 
458  mIndividualLayerSettings.insert( vlayer, IndividualLayerSettings( *enabledIt == QLatin1String( "enabled" ), t, tolIt->toDouble(), static_cast<QgsTolerance::UnitType>( tolUnitIt->toInt() ) ) );
459  }
460 
461  QString snapType = mProject->readEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapType" ), QStringLiteral( "off" ) );
462  mEnabled = true;
463  if ( snapType == QLatin1String( "to segment" ) )
464  mType = Segment;
465  else if ( snapType == QLatin1String( "to vertex and segment" ) )
466  mType = VertexAndSegment;
467  else if ( snapType == QLatin1String( "to vertex" ) )
468  mType = Vertex;
469  else if ( mMode != AdvancedConfiguration ) // Type is off but mode is advanced
470  {
471  mEnabled = false;
472  }
473 }
474 
476 {
477  return mProject;
478 }
479 
481 {
482  if ( mProject != project )
483  mProject = project;
484 
485  reset();
486 }
void setEnabled(bool enabled)
enables the snapping
SnappingMode
SnappingMode defines on which layer the snapping is performed.
bool valid() const
Returns if settings are valid.
void setEnabled(bool enabled)
enables the snapping
Base class for all map layer types.
Definition: qgsmaplayer.h:78
bool operator==(const QgsSnappingConfig &other) const
SnappingType type() const
Returns the type (vertices and/or segments)
QgsMapLayerType type() const
Returns the type of the layer.
SnappingMode mode() const
Returns the mode (all layers, active layer, per layer settings)
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
Both on vertices and segments.
bool operator!=(const QgsSnappingConfig::IndividualLayerSettings &other) const
Compare this configuration to other.
bool enabled() const
Returns if snapping is enabled.
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
bool operator!=(const QgsSnappingConfig &other) const
Compare this configuration to other.
UnitType
Type of unit of tolerance value from settings.
Definition: qgstolerance.h:40
QgsSnappingConfig(QgsProject *project=nullptr)
Constructor with default parameters defined in global settings.
QgsTolerance::UnitType units() const
Returns the type of units.
On all vector layers.
void reset()
reset to default values
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
void setIndividualLayerSettings(QgsVectorLayer *vl, const QgsSnappingConfig::IndividualLayerSettings &individualLayerSettings)
Sets individual layer snappings settings (applied if mode is AdvancedConfiguration) ...
bool intersectionSnapping() const
Returns if the snapping on intersection is enabled.
void setUnits(QgsTolerance::UnitType units)
Sets the type of units.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
static const double DEFAULT_SNAP_TOLERANCE
Default snapping distance tolerance.
Definition: qgis.h:145
void setIntersectionSnapping(bool enabled)
Sets if the snapping on intersection is enabled.
QgsTolerance::UnitType units() const
Returns the type of units.
bool operator==(const QgsSnappingConfig::IndividualLayerSettings &other) const
double tolerance() const
Returns the tolerance.
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
Reads and writes project states.
Definition: qgsproject.h:89
This is a container of advanced configuration (per layer) of the snapping of the project.
On a per layer configuration basis.
static const QgsTolerance::UnitType DEFAULT_SNAP_UNITS
Default snapping distance units.
Definition: qgis.h:151
void setTolerance(double tolerance)
Sets the tolerance.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
void setType(QgsSnappingConfig::SnappingType type)
define the type of snapping
bool removeLayers(const QList< QgsMapLayer *> &layers)
Removes the specified layers from the individual layer configuration.
Layer unit value.
Definition: qgstolerance.h:43
void setMode(SnappingMode mode)
define the mode of snapping
SnappingType
SnappingType defines on what object the snapping is performed.
Map (project) units. Added in 2.8.
Definition: qgstolerance.h:47
IndividualLayerSettings()=default
Constructs an invalid setting.
void setType(SnappingType type)
define the type of snapping
QgsSnappingConfig::SnappingType type() const
Returns the type (vertices and/or segments)
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
Definition: qgssettings.h:244
bool enabled() const
Returns if snapping is enabled.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
double tolerance() const
Returns the tolerance.
This is a container for configuration of the snapping of the project.
void setUnits(QgsTolerance::UnitType units)
Sets the type of units.
void setProject(QgsProject *project)
The project from which the snapped layers should be retrieved.
QgsProject * project() const
The project from which the snapped layers should be retrieved.
bool addLayers(const QList< QgsMapLayer *> &layers)
Adds the specified layers as individual layers to the configuration with standard configuration...
QHash< QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings > individualLayerSettings() const
Returns individual snapping settings for all layers.
Represents a vector layer which manages a vector based data sets.
void setTolerance(double tolerance)
Sets the tolerance.