QGIS API Documentation  3.0.2-Girona (307d082)
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 = ( SnappingType )QgsSettings().value( QStringLiteral( "/qgis/digitizing/default_snap_type" ), Vertex ).toInt();
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.
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection, bool flag=false) const
Return the setting value for a setting based on an enum.
Definition: qgssettings.h:231
bool valid() const
return if settings are valid
void setEnabled(bool enabled)
enables the snapping
Base class for all map layer types.
Definition: qgsmaplayer.h:56
bool operator==(const QgsSnappingConfig &other) const
SnappingType type() const
return the type (vertices and/or segments)
SnappingMode mode() const
return 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:57
Both on vertices and segments.
bool operator!=(const QgsSnappingConfig::IndividualLayerSettings &other) const
Compare this configuration to other.
bool enabled() const
return if snapping is enabled
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
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
return 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)
set individual layer snappings settings (applied if mode is AdvancedConfiguration) ...
bool intersectionSnapping() const
return if the snapping on intersection is enabled
void setUnits(QgsTolerance::UnitType units)
set 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)
set if the snapping on intersection is enabled
QgsTolerance::UnitType units() const
return the type of units
bool operator==(const QgsSnappingConfig::IndividualLayerSettings &other) const
double tolerance() const
return 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:82
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)
set 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
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
IndividualLayerSettings()=default
Constructs an invalid setting.
void setType(SnappingType type)
define the type of snapping
QgsSnappingConfig::SnappingType type() const
return the type (vertices and/or segments)
bool enabled() const
return 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
return the tolerance
This is a container for configuration of the snapping of the project.
void setUnits(QgsTolerance::UnitType units)
set 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
return individual snapping settings for all layers
Represents a vector layer which manages a vector based data sets.
void setTolerance(double tolerance)
set the tolerance