QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 : [email protected]
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  if ( mProject )
151  {
152  mIndividualLayerSettings = QHash<QgsVectorLayer *, IndividualLayerSettings>();
153  const auto constMapLayers = mProject->mapLayers();
154  for ( QgsMapLayer *ml : constMapLayers )
155  {
156  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
157  if ( vl )
158  {
159  mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
160  }
161  }
162  }
163 }
164 
166 {
167  return mEnabled;
168 }
169 
171 {
172  if ( mEnabled == enabled )
173  {
174  return;
175  }
176  mEnabled = enabled;
177 }
178 
180 {
181  return mMode;
182 }
183 
185 {
186  if ( mMode == mode )
187  {
188  return;
189  }
190  mMode = mode;
191 }
192 
194 {
195  return mType;
196 }
197 
199 {
200  if ( mType == type )
201  {
202  return;
203  }
204  mType = type;
205 }
206 
208 {
209  return mTolerance;
210 }
211 
213 {
214  if ( mTolerance == tolerance )
215  {
216  return;
217  }
218  mTolerance = tolerance;
219 }
220 
222 {
223  return mUnits;
224 }
225 
227 {
228  if ( mUnits == units )
229  {
230  return;
231  }
232  mUnits = units;
233 }
234 
236 {
237  return mIntersectionSnapping;
238 }
239 
241 {
242  mIntersectionSnapping = enabled;
243 }
244 
245 QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> QgsSnappingConfig::individualLayerSettings() const
246 {
247  return mIndividualLayerSettings;
248 }
249 
251 {
252  if ( vl && mIndividualLayerSettings.contains( vl ) )
253  {
254  return mIndividualLayerSettings.value( vl );
255  }
256  else
257  {
258  // return invalid settings
259  return IndividualLayerSettings();
260  }
261 }
262 
264 {
265  if ( !vl || !vl->isSpatial() || mIndividualLayerSettings.value( vl ) == individualLayerSettings )
266  {
267  return;
268  }
269  mIndividualLayerSettings.insert( vl, individualLayerSettings );
270 }
271 
273 {
274  return mEnabled != other.mEnabled
275  || mMode != other.mMode
276  || mType != other.mType
277  || mTolerance != other.mTolerance
278  || mUnits != other.mUnits
279  || mIndividualLayerSettings != other.mIndividualLayerSettings;
280 }
281 
282 void QgsSnappingConfig::readProject( const QDomDocument &doc )
283 {
284  QDomElement snapSettingsElem = doc.firstChildElement( QStringLiteral( "qgis" ) ).firstChildElement( QStringLiteral( "snapping-settings" ) );
285  if ( snapSettingsElem.isNull() )
286  {
287  readLegacySettings();
288  return;
289  }
290 
291  if ( snapSettingsElem.hasAttribute( QStringLiteral( "enabled" ) ) )
292  mEnabled = snapSettingsElem.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "1" );
293 
294  if ( snapSettingsElem.hasAttribute( QStringLiteral( "mode" ) ) )
295  mMode = ( SnappingMode )snapSettingsElem.attribute( QStringLiteral( "mode" ) ).toInt();
296 
297  if ( snapSettingsElem.hasAttribute( QStringLiteral( "type" ) ) )
298  mType = ( SnappingType )snapSettingsElem.attribute( QStringLiteral( "type" ) ).toInt();
299 
300  if ( snapSettingsElem.hasAttribute( QStringLiteral( "tolerance" ) ) )
301  mTolerance = snapSettingsElem.attribute( QStringLiteral( "tolerance" ) ).toDouble();
302 
303  if ( snapSettingsElem.hasAttribute( QStringLiteral( "unit" ) ) )
304  mUnits = ( QgsTolerance::UnitType )snapSettingsElem.attribute( QStringLiteral( "unit" ) ).toInt();
305 
306  if ( snapSettingsElem.hasAttribute( QStringLiteral( "intersection-snapping" ) ) )
307  mIntersectionSnapping = snapSettingsElem.attribute( QStringLiteral( "intersection-snapping" ) ) == QLatin1String( "1" );
308 
309  // do not clear the settings as they must be automatically synchronized with current layers
310  QDomNodeList nodes = snapSettingsElem.elementsByTagName( QStringLiteral( "individual-layer-settings" ) );
311  if ( nodes.count() )
312  {
313  QDomNode node = nodes.item( 0 );
314  QDomNodeList settingNodes = node.childNodes();
315  int layerCount = settingNodes.count();
316  for ( int i = 0; i < layerCount; ++i )
317  {
318  QDomElement settingElement = settingNodes.at( i ).toElement();
319  if ( settingElement.tagName() != QLatin1String( "layer-setting" ) )
320  {
321  QgsLogger::warning( QApplication::translate( "QgsProjectSnappingSettings", "Cannot read individual settings. Unexpected tag '%1'" ).arg( settingElement.tagName() ) );
322  continue;
323  }
324 
325  QString layerId = settingElement.attribute( QStringLiteral( "id" ) );
326  bool enabled = settingElement.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "1" );
327  SnappingType type = ( SnappingType )settingElement.attribute( QStringLiteral( "type" ) ).toInt();
328  double tolerance = settingElement.attribute( QStringLiteral( "tolerance" ) ).toDouble();
329  QgsTolerance::UnitType units = ( QgsTolerance::UnitType )settingElement.attribute( QStringLiteral( "units" ) ).toInt();
330 
331  QgsMapLayer *ml = mProject->mapLayer( layerId );
332  if ( !ml || ml->type() != QgsMapLayerType::VectorLayer )
333  continue;
334 
335  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
336 
337  IndividualLayerSettings setting = IndividualLayerSettings( enabled, type, tolerance, units );
338  mIndividualLayerSettings.insert( vl, setting );
339  }
340  }
341 }
342 
343 void QgsSnappingConfig::writeProject( QDomDocument &doc )
344 {
345  QDomElement snapSettingsElem = doc.createElement( QStringLiteral( "snapping-settings" ) );
346  snapSettingsElem.setAttribute( QStringLiteral( "enabled" ), QString::number( mEnabled ) );
347  snapSettingsElem.setAttribute( QStringLiteral( "mode" ), static_cast<int>( mMode ) );
348  snapSettingsElem.setAttribute( QStringLiteral( "type" ), static_cast<int>( mType ) );
349  snapSettingsElem.setAttribute( QStringLiteral( "tolerance" ), mTolerance );
350  snapSettingsElem.setAttribute( QStringLiteral( "unit" ), static_cast<int>( mUnits ) );
351  snapSettingsElem.setAttribute( QStringLiteral( "intersection-snapping" ), QString::number( mIntersectionSnapping ) );
352 
353  QDomElement ilsElement = doc.createElement( QStringLiteral( "individual-layer-settings" ) );
354  QHash<QgsVectorLayer *, IndividualLayerSettings>::const_iterator layerIt = mIndividualLayerSettings.constBegin();
355  for ( ; layerIt != mIndividualLayerSettings.constEnd(); ++layerIt )
356  {
357  const IndividualLayerSettings &setting = layerIt.value();
358 
359  QDomElement layerElement = doc.createElement( QStringLiteral( "layer-setting" ) );
360  layerElement.setAttribute( QStringLiteral( "id" ), layerIt.key()->id() );
361  layerElement.setAttribute( QStringLiteral( "enabled" ), QString::number( setting.enabled() ) );
362  layerElement.setAttribute( QStringLiteral( "type" ), static_cast<int>( setting.type() ) );
363  layerElement.setAttribute( QStringLiteral( "tolerance" ), setting.tolerance() );
364  layerElement.setAttribute( QStringLiteral( "units" ), static_cast<int>( setting.units() ) );
365  ilsElement.appendChild( layerElement );
366  }
367  snapSettingsElem.appendChild( ilsElement );
368 
369  doc.firstChildElement( QStringLiteral( "qgis" ) ).appendChild( snapSettingsElem );
370 }
371 
372 bool QgsSnappingConfig::addLayers( const QList<QgsMapLayer *> &layers )
373 {
374  bool changed = false;
375  bool enabled = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snap_enabled" ), true ).toBool();
376  SnappingType type = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snap_type" ), Vertex );
377  double tolerance = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance" ), Qgis::DEFAULT_SNAP_TOLERANCE ).toDouble();
378  QgsTolerance::UnitType units = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance_unit" ), Qgis::DEFAULT_SNAP_UNITS );
379 
380  const auto constLayers = layers;
381  for ( QgsMapLayer *ml : constLayers )
382  {
383  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
384  if ( vl && vl->isSpatial() )
385  {
386  mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
387  changed = true;
388  }
389  }
390  return changed;
391 }
392 
393 bool QgsSnappingConfig::removeLayers( const QList<QgsMapLayer *> &layers )
394 {
395  bool changed = false;
396  const auto constLayers = layers;
397  for ( QgsMapLayer *ml : constLayers )
398  {
399  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
400  if ( vl )
401  {
402  mIndividualLayerSettings.remove( vl );
403  changed = true;
404  }
405  }
406  return changed;
407 }
408 
409 void QgsSnappingConfig::readLegacySettings()
410 {
411  //
412  mMode = ActiveLayer;
413 
414  QString snapMode = mProject->readEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/SnappingMode" ) );
415 
416  mTolerance = mProject->readDoubleEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapTolerance" ), 0 );
417  mUnits = static_cast< QgsTolerance::UnitType >( mProject->readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapToleranceUnit" ), QgsTolerance::ProjectUnits ) );
418 
419  mIntersectionSnapping = mProject->readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/IntersectionSnapping" ), 0 );
420 
421  //read snapping settings from project
422  QStringList layerIdList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingList" ), QStringList() );
423  QStringList enabledList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingEnabledList" ), QStringList() );
424  QStringList toleranceList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingToleranceList" ), QStringList() );
425  QStringList toleranceUnitList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingToleranceUnitList" ), QStringList() );
426  QStringList snapToList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnapToList" ), QStringList() );
427 
428  // lists must have the same size, otherwise something is wrong
429  if ( layerIdList.size() != enabledList.size() ||
430  layerIdList.size() != toleranceList.size() ||
431  layerIdList.size() != toleranceUnitList.size() ||
432  layerIdList.size() != snapToList.size() )
433  return;
434 
435  // Use snapping information from the project
436  if ( snapMode == QLatin1String( "current_layer" ) )
437  mMode = ActiveLayer;
438  else if ( snapMode == QLatin1String( "all_layers" ) )
439  mMode = AllLayers;
440  else // either "advanced" or empty (for background compatibility)
441  mMode = AdvancedConfiguration;
442 
443  // load layers, tolerances, snap type
444  QStringList::const_iterator layerIt( layerIdList.constBegin() );
445  QStringList::const_iterator tolIt( toleranceList.constBegin() );
446  QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
447  QStringList::const_iterator snapIt( snapToList.constBegin() );
448  QStringList::const_iterator enabledIt( enabledList.constBegin() );
449  for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
450  {
451  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mProject->mapLayer( *layerIt ) );
452  if ( !vlayer || !vlayer->isSpatial() )
453  continue;
454 
455  SnappingType t( *snapIt == QLatin1String( "to_vertex" ) ? Vertex :
456  ( *snapIt == QLatin1String( "to_segment" ) ? Segment :
458  )
459  );
460 
461  mIndividualLayerSettings.insert( vlayer, IndividualLayerSettings( *enabledIt == QLatin1String( "enabled" ), t, tolIt->toDouble(), static_cast<QgsTolerance::UnitType>( tolUnitIt->toInt() ) ) );
462  }
463 
464  QString snapType = mProject->readEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapType" ), QStringLiteral( "off" ) );
465  mEnabled = true;
466  if ( snapType == QLatin1String( "to segment" ) )
467  mType = Segment;
468  else if ( snapType == QLatin1String( "to vertex and segment" ) )
469  mType = VertexAndSegment;
470  else if ( snapType == QLatin1String( "to vertex" ) )
471  mType = Vertex;
472  else if ( mMode != AdvancedConfiguration ) // Type is off but mode is advanced
473  {
474  mEnabled = false;
475  }
476 }
477 
479 {
480  return mProject;
481 }
482 
484 {
485  if ( mProject != project )
486  mProject = project;
487 
488  reset();
489 }
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:79
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:122
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:188
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:91
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:194
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:245
bool enabled() const
Returns if snapping is enabled.
Q_INVOKABLE 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.