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