QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
26 
28  : mValid( true )
29  , mEnabled( enabled )
30  , mType( type )
31  , mTolerance( tolerance )
32  , mUnits( units )
33 {}
34 
36 {
37  return mValid;
38 }
39 
41 {
42  return mEnabled;
43 }
44 
46 {
47  mEnabled = enabled;
48 }
49 
51 {
52  return mType;
53 }
54 
56 {
57  mType = type;
58 }
59 
61 {
62  return mTolerance;
63 }
64 
66 {
67  mTolerance = tolerance;
68 }
69 
71 {
72  return mUnits;
73 }
74 
76 {
77  mUnits = units;
78 }
79 
81 {
82  return mValid != other.mValid
83  || mEnabled != other.mEnabled
84  || mType != other.mType
85  || mTolerance != other.mTolerance
86  || mUnits != other.mUnits;
87 }
88 
90 {
91  return mValid == other.mValid
92  && mEnabled == other.mEnabled
93  && mType == other.mType
94  && mTolerance == other.mTolerance
95  && mUnits == other.mUnits;
96 }
97 
98 
100  : mProject( project )
101 {
102  if ( project )
103  reset();
104 }
105 
107 {
108  return mEnabled == other.mEnabled
109  && mMode == other.mMode
110  && mType == other.mType
111  && mTolerance == other.mTolerance
112  && mUnits == other.mUnits
113  && mIntersectionSnapping == other.mIntersectionSnapping
114  && mIndividualLayerSettings == other.mIndividualLayerSettings;
115 }
116 
118 {
119  // get defaults values. They are both used for standard and advanced configuration (per layer)
120  bool enabled = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snap_enabled" ), false ).toBool();
121  SnappingMode mode = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snap_mode" ), AllLayers );
122  if ( mode == 0 )
123  {
124  // backward compatibility with QGIS 2.x
125  // could be removed in 3.4+
126  mode = AllLayers;
127  }
128  SnappingType type = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snap_type" ), Vertex );
129  double tolerance = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance" ), Qgis::DEFAULT_SNAP_TOLERANCE ).toDouble();
130  QgsTolerance::UnitType units = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance_unit" ), Qgis::DEFAULT_SNAP_UNITS );
131 
132  // assign main (standard) config
133  mEnabled = enabled;
134  mMode = mode;
135  mType = type;
136  mTolerance = tolerance;
137  // do not allow unit to be "layer" if not in advanced configuration
138  if ( mUnits == QgsTolerance::LayerUnits && mMode != AdvancedConfiguration )
139  {
141  }
142  else
143  {
144  mUnits = units;
145  }
146  mIntersectionSnapping = false;
147 
148  // set advanced config
149  mIndividualLayerSettings = QHash<QgsVectorLayer *, IndividualLayerSettings>();
150  Q_FOREACH ( QgsMapLayer *ml, mProject->mapLayers() )
151  {
152  QgsVectorLayer *vl = dynamic_cast<QgsVectorLayer *>( ml );
153  if ( vl )
154  {
155  mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
156  }
157  }
158 }
159 
161 {
162  return mEnabled;
163 }
164 
166 {
167  if ( mEnabled == enabled )
168  {
169  return;
170  }
171  mEnabled = enabled;
172 }
173 
175 {
176  return mMode;
177 }
178 
180 {
181  if ( mMode == mode )
182  {
183  return;
184  }
185  mMode = mode;
186 }
187 
189 {
190  return mType;
191 }
192 
194 {
195  if ( mType == type )
196  {
197  return;
198  }
199  mType = type;
200 }
201 
203 {
204  return mTolerance;
205 }
206 
208 {
209  if ( mTolerance == tolerance )
210  {
211  return;
212  }
213  mTolerance = tolerance;
214 }
215 
217 {
218  return mUnits;
219 }
220 
222 {
223  if ( mUnits == units )
224  {
225  return;
226  }
227  mUnits = units;
228 }
229 
231 {
232  return mIntersectionSnapping;
233 }
234 
236 {
237  mIntersectionSnapping = enabled;
238 }
239 
240 QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> QgsSnappingConfig::individualLayerSettings() const
241 {
242  return mIndividualLayerSettings;
243 }
244 
246 {
247  if ( vl && mIndividualLayerSettings.contains( vl ) )
248  {
249  return mIndividualLayerSettings.value( vl );
250  }
251  else
252  {
253  // return invalid settings
254  return IndividualLayerSettings();
255  }
256 }
257 
259 {
260  if ( !vl || !vl->isSpatial() || mIndividualLayerSettings.value( vl ) == individualLayerSettings )
261  {
262  return;
263  }
264  mIndividualLayerSettings.insert( vl, individualLayerSettings );
265 }
266 
268 {
269  return mEnabled != other.mEnabled
270  || mMode != other.mMode
271  || mType != other.mType
272  || mTolerance != other.mTolerance
273  || mUnits != other.mUnits
274  || mIndividualLayerSettings != other.mIndividualLayerSettings;
275 }
276 
277 void QgsSnappingConfig::readProject( const QDomDocument &doc )
278 {
279  QDomElement snapSettingsElem = doc.firstChildElement( QStringLiteral( "qgis" ) ).firstChildElement( QStringLiteral( "snapping-settings" ) );
280  if ( snapSettingsElem.isNull() )
281  {
282  readLegacySettings();
283  return;
284  }
285 
286  if ( snapSettingsElem.hasAttribute( QStringLiteral( "enabled" ) ) )
287  mEnabled = snapSettingsElem.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "1" );
288 
289  if ( snapSettingsElem.hasAttribute( QStringLiteral( "mode" ) ) )
290  mMode = ( SnappingMode )snapSettingsElem.attribute( QStringLiteral( "mode" ) ).toInt();
291 
292  if ( snapSettingsElem.hasAttribute( QStringLiteral( "type" ) ) )
293  mType = ( SnappingType )snapSettingsElem.attribute( QStringLiteral( "type" ) ).toInt();
294 
295  if ( snapSettingsElem.hasAttribute( QStringLiteral( "tolerance" ) ) )
296  mTolerance = snapSettingsElem.attribute( QStringLiteral( "tolerance" ) ).toDouble();
297 
298  if ( snapSettingsElem.hasAttribute( QStringLiteral( "unit" ) ) )
299  mUnits = ( QgsTolerance::UnitType )snapSettingsElem.attribute( QStringLiteral( "unit" ) ).toInt();
300 
301  if ( snapSettingsElem.hasAttribute( QStringLiteral( "intersection-snapping" ) ) )
302  mIntersectionSnapping = snapSettingsElem.attribute( QStringLiteral( "intersection-snapping" ) ) == QLatin1String( "1" );
303 
304  // do not clear the settings as they must be automatically synchronized with current layers
305  QDomNodeList nodes = snapSettingsElem.elementsByTagName( QStringLiteral( "individual-layer-settings" ) );
306  if ( nodes.count() )
307  {
308  QDomNode node = nodes.item( 0 );
309  QDomNodeList settingNodes = node.childNodes();
310  int layerCount = settingNodes.count();
311  for ( int i = 0; i < layerCount; ++i )
312  {
313  QDomElement settingElement = settingNodes.at( i ).toElement();
314  if ( settingElement.tagName() != QLatin1String( "layer-setting" ) )
315  {
316  QgsLogger::warning( QApplication::translate( "QgsProjectSnappingSettings", "Cannot read individual settings. Unexpected tag '%1'" ).arg( settingElement.tagName() ) );
317  continue;
318  }
319 
320  QString layerId = settingElement.attribute( QStringLiteral( "id" ) );
321  bool enabled = settingElement.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "1" );
322  SnappingType type = ( SnappingType )settingElement.attribute( QStringLiteral( "type" ) ).toInt();
323  double tolerance = settingElement.attribute( QStringLiteral( "tolerance" ) ).toDouble();
324  QgsTolerance::UnitType units = ( QgsTolerance::UnitType )settingElement.attribute( QStringLiteral( "units" ) ).toInt();
325 
326  QgsMapLayer *ml = mProject->mapLayer( layerId );
327  if ( !ml || ml->type() != QgsMapLayer::VectorLayer )
328  continue;
329 
330  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
331 
332  IndividualLayerSettings setting = IndividualLayerSettings( enabled, type, tolerance, units );
333  mIndividualLayerSettings.insert( vl, setting );
334  }
335  }
336 }
337 
338 void QgsSnappingConfig::writeProject( QDomDocument &doc )
339 {
340  QDomElement snapSettingsElem = doc.createElement( QStringLiteral( "snapping-settings" ) );
341  snapSettingsElem.setAttribute( QStringLiteral( "enabled" ), QString::number( mEnabled ) );
342  snapSettingsElem.setAttribute( QStringLiteral( "mode" ), ( int )mMode );
343  snapSettingsElem.setAttribute( QStringLiteral( "type" ), ( int )mType );
344  snapSettingsElem.setAttribute( QStringLiteral( "tolerance" ), mTolerance );
345  snapSettingsElem.setAttribute( QStringLiteral( "unit" ), ( int )mUnits );
346  snapSettingsElem.setAttribute( QStringLiteral( "intersection-snapping" ), QString::number( mIntersectionSnapping ) );
347 
348  QDomElement ilsElement = doc.createElement( QStringLiteral( "individual-layer-settings" ) );
349  QHash<QgsVectorLayer *, IndividualLayerSettings>::const_iterator layerIt = mIndividualLayerSettings.constBegin();
350  for ( ; layerIt != mIndividualLayerSettings.constEnd(); ++layerIt )
351  {
352  const IndividualLayerSettings &setting = layerIt.value();
353 
354  QDomElement layerElement = doc.createElement( QStringLiteral( "layer-setting" ) );
355  layerElement.setAttribute( QStringLiteral( "id" ), layerIt.key()->id() );
356  layerElement.setAttribute( QStringLiteral( "enabled" ), QString::number( setting.enabled() ) );
357  layerElement.setAttribute( QStringLiteral( "type" ), ( int )setting.type() );
358  layerElement.setAttribute( QStringLiteral( "tolerance" ), setting.tolerance() );
359  layerElement.setAttribute( QStringLiteral( "units" ), ( int )setting.units() );
360  ilsElement.appendChild( layerElement );
361  }
362  snapSettingsElem.appendChild( ilsElement );
363 
364  doc.firstChildElement( QStringLiteral( "qgis" ) ).appendChild( snapSettingsElem );
365 }
366 
367 bool QgsSnappingConfig::addLayers( const QList<QgsMapLayer *> &layers )
368 {
369  bool changed = false;
370  bool enabled = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snap_enabled" ), true ).toBool();
371  SnappingType type = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snap_type" ), Vertex );
372  double tolerance = QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance" ), Qgis::DEFAULT_SNAP_TOLERANCE ).toDouble();
373  QgsTolerance::UnitType units = QgsSettings().enumValue( QStringLiteral( "/qgis/digitizing/default_snapping_tolerance_unit" ), Qgis::DEFAULT_SNAP_UNITS );
374 
375  Q_FOREACH ( QgsMapLayer *ml, layers )
376  {
377  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
378  if ( vl && vl->isSpatial() )
379  {
380  mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
381  changed = true;
382  }
383  }
384  return changed;
385 }
386 
387 bool QgsSnappingConfig::removeLayers( const QList<QgsMapLayer *> &layers )
388 {
389  bool changed = false;
390  Q_FOREACH ( QgsMapLayer *ml, layers )
391  {
392  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
393  if ( vl )
394  {
395  mIndividualLayerSettings.remove( vl );
396  changed = true;
397  }
398  }
399  return changed;
400 }
401 
402 void QgsSnappingConfig::readLegacySettings()
403 {
404  //
405  mMode = ActiveLayer;
406 
407  QString snapMode = mProject->readEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/SnappingMode" ) );
408 
409  mTolerance = mProject->readDoubleEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapTolerance" ), 0 );
410  mUnits = static_cast< QgsTolerance::UnitType >( mProject->readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapToleranceUnit" ), QgsTolerance::ProjectUnits ) );
411 
412  mIntersectionSnapping = mProject->readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/IntersectionSnapping" ), 0 );
413 
414  //read snapping settings from project
415  QStringList layerIdList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingList" ), QStringList() );
416  QStringList enabledList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingEnabledList" ), QStringList() );
417  QStringList toleranceList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingToleranceList" ), QStringList() );
418  QStringList toleranceUnitList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnappingToleranceUnitList" ), QStringList() );
419  QStringList snapToList = mProject->readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/LayerSnapToList" ), QStringList() );
420 
421  // lists must have the same size, otherwise something is wrong
422  if ( layerIdList.size() != enabledList.size() ||
423  layerIdList.size() != toleranceList.size() ||
424  layerIdList.size() != toleranceUnitList.size() ||
425  layerIdList.size() != snapToList.size() )
426  return;
427 
428  // Use snapping information from the project
429  if ( snapMode == QLatin1String( "current_layer" ) )
430  mMode = ActiveLayer;
431  else if ( snapMode == QLatin1String( "all_layers" ) )
432  mMode = AllLayers;
433  else // either "advanced" or empty (for background compatibility)
434  mMode = AdvancedConfiguration;
435 
436  // load layers, tolerances, snap type
437  QStringList::const_iterator layerIt( layerIdList.constBegin() );
438  QStringList::const_iterator tolIt( toleranceList.constBegin() );
439  QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
440  QStringList::const_iterator snapIt( snapToList.constBegin() );
441  QStringList::const_iterator enabledIt( enabledList.constBegin() );
442  for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
443  {
444  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mProject->mapLayer( *layerIt ) );
445  if ( !vlayer || !vlayer->isSpatial() )
446  continue;
447 
448  SnappingType t( *snapIt == QLatin1String( "to_vertex" ) ? Vertex :
449  ( *snapIt == QLatin1String( "to_segment" ) ? Segment :
451  )
452  );
453 
454  mIndividualLayerSettings.insert( vlayer, IndividualLayerSettings( *enabledIt == QLatin1String( "enabled" ), t, tolIt->toDouble(), static_cast<QgsTolerance::UnitType>( tolUnitIt->toInt() ) ) );
455  }
456 
457  QString snapType = mProject->readEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/DefaultSnapType" ), QStringLiteral( "off" ) );
458  mEnabled = true;
459  if ( snapType == QLatin1String( "to segment" ) )
460  mType = Segment;
461  else if ( snapType == QLatin1String( "to vertex and segment" ) )
462  mType = VertexAndSegment;
463  else if ( snapType == QLatin1String( "to vertex" ) )
464  mType = Vertex;
465  else if ( mMode != AdvancedConfiguration ) // Type is off but mode is advanced
466  {
467  mEnabled = false;
468  }
469 }
470 
472 {
473  return mProject;
474 }
475 
477 {
478  if ( mProject != project )
479  mProject = project;
480 
481  reset();
482 }
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:61
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.
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
On all vector layers.
QgsMapLayer::LayerType type() const
Returns the type of the layer.
void reset()
reset to default values
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:157
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.
bool isSpatial() const override
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
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:85
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:163
void setTolerance(double tolerance)
Sets the tolerance.
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:235
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.