Use toolbar or status bar for snapping config
@ -574,6 +574,15 @@
|
||||
<file>themes/default/mIconClearText.svg</file>
|
||||
<file>themes/default/mIconClearTextHover.svg</file>
|
||||
<file>themes/default/rendererPointClusterSymbol.svg</file>
|
||||
<file>themes/default/mIconSnapping.svg</file>
|
||||
<file>themes/default/mIconSnappingActiveLayer.svg</file>
|
||||
<file>themes/default/mIconSnappingAdvanced.svg</file>
|
||||
<file>themes/default/mIconSnappingAllLayers.svg</file>
|
||||
<file>themes/default/mIconSnappingVertexAndSegment.svg</file>
|
||||
<file>themes/default/mIconSnappingVertex.svg</file>
|
||||
<file>themes/default/mIconSnappingSegment.svg</file>
|
||||
<file>themes/default/mIconTopologicalEditing.svg</file>
|
||||
<file>themes/default/mIconSnappingIntersection.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/images/tips">
|
||||
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
|
||||
|
105
images/themes/default/mIconSnapping.svg
Normal file
@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generated by IcoMoon.io -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
id="svg4136"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="mIconSnapping.svg">
|
||||
<metadata
|
||||
id="metadata4154">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs4152" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1343"
|
||||
id="namedview4150"
|
||||
showgrid="true"
|
||||
inkscape:zoom="25.875689"
|
||||
inkscape:cx="-2.0772384"
|
||||
inkscape:cy="12.333477"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4136">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5544" />
|
||||
</sodipodi:namedview>
|
||||
<g
|
||||
id="g4138"
|
||||
transform="translate(0,-488)" />
|
||||
<path
|
||||
d="m 366.643,-210.599 c 0,29.563 -11.51,57.365 -32.379,78.244 -20.941,20.921 -48.712,32.44 -78.285,32.44 -61.02,0 -110.663,-49.644 -110.663,-110.684 l 0,-124.099 -99.052,0 0,124.099 c 0,115.682 94.085,209.715 209.715,209.715 56.033,0 108.718,-21.781 148.326,-61.379 39.608,-39.639 61.43,-92.324 61.43,-148.337 l 0,-124.099 -99.103,0 0,124.099 z"
|
||||
id="path4140"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000" />
|
||||
<path
|
||||
d="m 384.645,-379.6 0,-9.922 -9.882,0 z"
|
||||
id="path4142"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000" />
|
||||
<path
|
||||
d="m 454.523,-349.422 9.913,0 0,-113.695 -97.577,0 0,113.695 87.664,0 z m -67.84,-91.935 57.938,0 0,72.837 -57.938,0 0,-72.837 z"
|
||||
id="path4144"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000" />
|
||||
<path
|
||||
d="m 251.453,-361.382 99.871,-84.582 -84.316,44.667 -24.852,-47.698 -83.978,67.154 75.111,-25.794 z"
|
||||
id="path4146"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000" />
|
||||
<path
|
||||
d="m 136.233,-349.422 9.892,0 0,-113.695 -97.546,0 0,113.695 87.654,0 z m -67.86,-91.935 57.948,0 0,72.837 -57.948,0 0,-72.837 z"
|
||||
id="path4148"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000" />
|
||||
<rect
|
||||
style="display:inline;opacity:1;fill:#bebebe;fill-opacity:1;stroke:#8c8c8c;stroke-width:1.24999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4563-1"
|
||||
width="3.75"
|
||||
height="3.75"
|
||||
x="2.625"
|
||||
y="2.375" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#8c8c8c;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 19.5,8.75 0,6 c 0,3 -2,5 -5,5 l -5,0 c -3,0 -5,-2 -5,-5 l 0,-6"
|
||||
id="path5546"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<rect
|
||||
style="display:inline;opacity:1;fill:#bebebe;fill-opacity:1;stroke:#8c8c8c;stroke-width:1.24999988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4563-1-3"
|
||||
width="3.75"
|
||||
height="3.75"
|
||||
x="17.625"
|
||||
y="2.375" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
1133
images/themes/default/mIconSnappingActiveLayer.svg
Normal file
After Width: | Height: | Size: 37 KiB |
1363
images/themes/default/mIconSnappingAdvanced.svg
Normal file
After Width: | Height: | Size: 47 KiB |
1167
images/themes/default/mIconSnappingAllLayers.svg
Normal file
After Width: | Height: | Size: 39 KiB |
1124
images/themes/default/mIconSnappingIntersection.svg
Normal file
After Width: | Height: | Size: 37 KiB |
1105
images/themes/default/mIconSnappingSegment.svg
Normal file
After Width: | Height: | Size: 36 KiB |
1120
images/themes/default/mIconSnappingVertex.svg
Normal file
After Width: | Height: | Size: 37 KiB |
1126
images/themes/default/mIconSnappingVertexAndSegment.svg
Normal file
After Width: | Height: | Size: 37 KiB |
1118
images/themes/default/mIconTopologicalEditing.svg
Normal file
After Width: | Height: | Size: 37 KiB |
@ -111,6 +111,7 @@
|
||||
%Include qgspointlocator.sip
|
||||
%Include qgsproject.sip
|
||||
%Include qgsprojectproperty.sip
|
||||
%Include qgssnappingconfig.sip
|
||||
%Include qgsprojectversion.sip
|
||||
%Include qgsprovidermetadata.sip
|
||||
%Include qgsproviderregistry.sip
|
||||
|
@ -65,8 +65,6 @@ class QgsMapLayerModel : QAbstractItemModel
|
||||
|
||||
/**
|
||||
* Returns strings for all roles supported by this model.
|
||||
*
|
||||
* @note Available only with Qt5 (python and c++)
|
||||
*/
|
||||
QHash<int, QByteArray> roleNames() const;
|
||||
|
||||
|
@ -274,14 +274,6 @@ class QgsProject : QObject
|
||||
*/
|
||||
QgsLayerTreeGroup* createEmbeddedGroup( const QString& groupName, const QString& projectFilePath, const QStringList &invisibleLayers );
|
||||
|
||||
/** Convenience function to set snap settings per layer */
|
||||
void setSnapSettingsForLayer( const QString& layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance,
|
||||
bool avoidIntersection );
|
||||
|
||||
/** Convenience function to query snap settings of a layer */
|
||||
bool snapSettingsForLayer( const QString& layerId, bool& enabled /Out/, QgsSnapper::SnappingType& type /Out/, QgsTolerance::UnitType& units /Out/, double& tolerance /Out/,
|
||||
bool& avoidIntersection /Out/ ) const;
|
||||
|
||||
/** Convenience function to set topological editing */
|
||||
void setTopologicalEditing( bool enabled );
|
||||
|
||||
@ -389,27 +381,14 @@ class QgsProject : QObject
|
||||
|
||||
QgsExpressionContext createExpressionContext() const;
|
||||
|
||||
protected:
|
||||
|
||||
/** Set error message from read/write operation
|
||||
* @note not available in Python bindings
|
||||
/**
|
||||
* The snapping configuration for this project.
|
||||
*/
|
||||
//void setError( const QString& errorMessage );
|
||||
|
||||
/** Clear error message
|
||||
* @note not available in Python bindings
|
||||
QgsSnappingConfig snappingConfig() const;
|
||||
/**
|
||||
* The snapping configuration for this project.
|
||||
*/
|
||||
//void clearError();
|
||||
|
||||
//! Creates layer and adds it to maplayer registry
|
||||
//! @note not available in python bindings
|
||||
// bool addLayer( const QDomElement& layerElem, QList<QDomNode>& brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList );
|
||||
|
||||
//! @note not available in python bindings
|
||||
// void initializeEmbeddedSubtree( const QString& projectFilePath, QgsLayerTreeGroup* group );
|
||||
|
||||
//! @note not available in python bindings
|
||||
// void loadEmbeddedNodes( QgsLayerTreeGroup* group );
|
||||
void setSnappingConfig( const QgsSnappingConfig& snappingConfig );
|
||||
|
||||
signals:
|
||||
//! emitted when project is being read
|
||||
@ -451,8 +430,6 @@ class QgsProject : QObject
|
||||
|
||||
void loadingLayer( const QString& );
|
||||
|
||||
void snapSettingsChanged();
|
||||
|
||||
//! Emitted when the list of layer which are excluded from map identification changes
|
||||
void nonIdentifiableLayersChanged( QStringList nonIdentifiableLayers );
|
||||
|
||||
@ -462,6 +439,9 @@ class QgsProject : QObject
|
||||
//! Emitted when the home path of the project changes
|
||||
void homePathChanged();
|
||||
|
||||
//! emitted whenever the configuration for snapping has changed
|
||||
void snappingConfigChanged();
|
||||
|
||||
/** Emitted whenever the expression variables stored in the project have been changed.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
|
238
python/core/qgssnappingconfig.sip
Normal file
@ -0,0 +1,238 @@
|
||||
/** \ingroup core
|
||||
* This is a container for configuration of the snapping of the project
|
||||
* @note added in 3.0
|
||||
*/
|
||||
class QgsSnappingConfig
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgssnappingconfig.h>
|
||||
%End
|
||||
public:
|
||||
/**
|
||||
* SnappingMode defines on which layer the snapping is performed
|
||||
*/
|
||||
enum SnappingMode
|
||||
{
|
||||
ActiveLayer, /*!< on the active layer */
|
||||
AllLayers, /*!< on all vector layers */
|
||||
AdvancedConfiguration, /*!< on a per layer configuration basis */
|
||||
};
|
||||
|
||||
/**
|
||||
* SnappingType defines on what object the snapping is performed
|
||||
*/
|
||||
enum SnappingType
|
||||
{
|
||||
Vertex, /*!< on vertices only */
|
||||
VertexAndSegment, /*!< both on vertices and segments */
|
||||
Segment, /*!< on segments only */
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* This is a container of advanced configuration (per layer) of the snapping of the project
|
||||
* @note added in 3.0
|
||||
*/
|
||||
class IndividualLayerSettings
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief IndividualLayerSettings
|
||||
* @param enabled
|
||||
* @param type
|
||||
* @param tolerance
|
||||
* @param units
|
||||
* @param avoidIntersection
|
||||
*/
|
||||
IndividualLayerSettings( bool enabled, QgsSnappingConfig::SnappingType type, double tolerance, QgsTolerance::UnitType units, bool avoidIntersection = false );
|
||||
|
||||
/**
|
||||
* Constructs an invalid setting
|
||||
*/
|
||||
IndividualLayerSettings();
|
||||
|
||||
//! return if settings are valid
|
||||
bool valid() const;
|
||||
|
||||
//! return if snaping is enbaled
|
||||
bool enabled() const;
|
||||
|
||||
//! enables the snapping
|
||||
void setEnabled( bool enabled );
|
||||
|
||||
//! return the type (vertices and/or segments)
|
||||
QgsSnappingConfig::SnappingType type() const;
|
||||
|
||||
//! define the type of snapping
|
||||
void setType( QgsSnappingConfig::SnappingType type );
|
||||
|
||||
//! return the tolerance
|
||||
double tolerance() const;
|
||||
|
||||
//! set the tolerance
|
||||
void setTolerance( double tolerance );
|
||||
|
||||
//! return the type of units
|
||||
QgsTolerance::UnitType units() const;
|
||||
|
||||
//! set the type of units
|
||||
void setUnits( QgsTolerance::UnitType units );
|
||||
|
||||
//! return if it shall avoid intersection (polygon layers only)
|
||||
bool avoidIntersection() const;
|
||||
|
||||
//! set if it shall avoid intersection (polygon layers only)
|
||||
void setAvoidIntersection( bool avoidIntersection );
|
||||
|
||||
/**
|
||||
* Compare this configuration to other.
|
||||
*/
|
||||
//bool operator!= ( const QgsSnappingConfig::IndividualLayerSettings& other ) const;
|
||||
|
||||
//bool operator== ( const QgsSnappingConfig::IndividualLayerSettings& other ) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor with default parameters defined in global settings
|
||||
*/
|
||||
explicit QgsSnappingConfig();
|
||||
|
||||
~QgsSnappingConfig();
|
||||
|
||||
//! reset to default values
|
||||
void reset();
|
||||
|
||||
//! return if snaping is enbaled
|
||||
bool enabled() const;
|
||||
|
||||
//! enables the snapping
|
||||
void setEnabled( bool enabled );
|
||||
|
||||
//! return the mode (all layers, active layer, per layer settings)
|
||||
QgsSnappingConfig::SnappingMode mode() const;
|
||||
|
||||
//! define the mode of snapping
|
||||
void setMode( QgsSnappingConfig::SnappingMode mode );
|
||||
|
||||
//! return the type (vertices and/or segments)
|
||||
QgsSnappingConfig::SnappingType type() const;
|
||||
|
||||
//! define the type of snapping
|
||||
void setType( QgsSnappingConfig::SnappingType type );
|
||||
|
||||
//! return the tolerance
|
||||
double tolerance() const;
|
||||
|
||||
//! set the tolerance
|
||||
void setTolerance( double tolerance );
|
||||
|
||||
//! return the type of units
|
||||
QgsTolerance::UnitType units() const;
|
||||
|
||||
//! set the type of units
|
||||
void setUnits( QgsTolerance::UnitType units );
|
||||
|
||||
//! return if the topological editing is enabled
|
||||
bool topologicalEditing() const;
|
||||
|
||||
//! set if the topological editing is enabled
|
||||
void setTopologicalEditing( bool enabled );
|
||||
|
||||
//! return if the snapping on intersection is enabled
|
||||
bool intersectionSnapping() const;
|
||||
|
||||
//! set if the snapping on intersection is enabled
|
||||
void setIntersectionSnapping( bool enabled );
|
||||
|
||||
//! return individual snapping settings for all layers
|
||||
SIP_PYDICT individualLayerSettings() const;
|
||||
%MethodCode
|
||||
// Create the dictionary.
|
||||
PyObject* d = PyDict_New();
|
||||
if (!d)
|
||||
return nullptr;
|
||||
// Set the dictionary elements.
|
||||
QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> container = sipCpp->individualLayerSettings();
|
||||
QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i = container.constBegin();
|
||||
while ( i != container.constEnd() )
|
||||
{
|
||||
QgsVectorLayer* vl = i.key();
|
||||
QgsSnappingConfig::IndividualLayerSettings* ils = new QgsSnappingConfig::IndividualLayerSettings( i.value() );
|
||||
|
||||
PyObject* vlobj = sipConvertFromType( vl, sipType_QgsVectorLayer, nullptr );
|
||||
PyObject* ilsobj = sipConvertFromType( ils, sipType_QgsSnappingConfig_IndividualLayerSettings, Py_None );
|
||||
|
||||
if ( !vlobj || !ilsobj || PyDict_SetItem( d, vlobj, ilsobj) < 0)
|
||||
{
|
||||
Py_DECREF( d );
|
||||
if ( vlobj )
|
||||
{
|
||||
Py_DECREF( vlobj );
|
||||
}
|
||||
if ( ilsobj )
|
||||
{
|
||||
Py_DECREF( ilsobj );
|
||||
}
|
||||
else
|
||||
{
|
||||
delete ils;
|
||||
}
|
||||
PyErr_SetString(PyExc_StopIteration,"");
|
||||
}
|
||||
Py_DECREF( vlobj );
|
||||
Py_DECREF( ilsobj );
|
||||
++i;
|
||||
}
|
||||
sipRes = d;
|
||||
%End
|
||||
|
||||
//! return individual layer snappings settings (applied if mode is AdvancedConfiguration)
|
||||
QgsSnappingConfig::IndividualLayerSettings individualLayerSettings( QgsVectorLayer* vl ) const;
|
||||
|
||||
//! set individual layer snappings settings (applied if mode is AdvancedConfiguration)
|
||||
void setIndividualLayerSettings( QgsVectorLayer* vl, QgsSnappingConfig::IndividualLayerSettings individualLayerSettings );
|
||||
|
||||
/**
|
||||
* Compare this configuration to other.
|
||||
*/
|
||||
bool operator!= ( const QgsSnappingConfig& other ) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Reads the configuration from the specified QGIS project document.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
void readProject( const QDomDocument& doc );
|
||||
|
||||
/**
|
||||
* Writes the configuration to the specified QGIS project document.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
void writeProject( QDomDocument& doc );
|
||||
|
||||
/**
|
||||
* Adds the specified layers as individual layers to the configuration
|
||||
* with standard configuration.
|
||||
* When implementing a long-living QgsSnappingConfig (like the one in QgsProject)
|
||||
* it is best to directly feed this with information from the layer registry.
|
||||
*
|
||||
* @return True if changes have been done.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
bool addLayers( const QList<QgsMapLayer*>& layers );
|
||||
|
||||
|
||||
/**
|
||||
* Removes the specified layers from the individual layer configuration.
|
||||
* When implementing a long-living QgsSnappingConfig (like the one in QgsProject)
|
||||
* it is best to directly feed this with information from the layer registry.
|
||||
*
|
||||
* @return True if changes have been done.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
bool removeLayers( const QList<QgsMapLayer*>& layers );
|
||||
|
||||
};
|
@ -25,10 +25,11 @@ class QgsSnappingUtils : QObject
|
||||
|
||||
/** Assign current map settings to the utils - used for conversion between screen coords to map coords */
|
||||
void setMapSettings( const QgsMapSettings& settings );
|
||||
const QgsMapSettings& mapSettings() const;
|
||||
QgsMapSettings mapSettings() const;
|
||||
|
||||
/** Set current layer so that if mode is SnapCurrentLayer we know which layer to use */
|
||||
void setCurrentLayer( QgsVectorLayer* layer );
|
||||
/** The current layer used if mode is SnapCurrentLayer */
|
||||
QgsVectorLayer* currentLayer() const;
|
||||
|
||||
|
||||
@ -42,11 +43,6 @@ class QgsSnappingUtils : QObject
|
||||
SnapAdvanced, //!< snap according to the configuration set in setLayers()
|
||||
};
|
||||
|
||||
/** Set how the snapping to map is done */
|
||||
void setSnapToMapMode( SnapToMapMode mode );
|
||||
/** Find out how the snapping to map is done */
|
||||
SnapToMapMode snapToMapMode() const;
|
||||
|
||||
enum IndexingStrategy
|
||||
{
|
||||
IndexAlwaysFull, //!< For all layers build index of full extent. Uses more memory, but queries are faster.
|
||||
@ -59,11 +55,6 @@ class QgsSnappingUtils : QObject
|
||||
/** Find out which strategy is used for indexing - by default hybrid indexing is used */
|
||||
IndexingStrategy indexingStrategy() const;
|
||||
|
||||
/** Configure options used when the mode is snap to current layer or to all layers */
|
||||
void setDefaultSettings( int type, double tolerance, QgsTolerance::UnitType unit );
|
||||
/** Query options used when the mode is snap to current layer or to all layers */
|
||||
void defaultSettings( int& type /Out/, double& tolerance /Out/, QgsTolerance::UnitType& unit /Out/ );
|
||||
|
||||
/**
|
||||
* Configures how a certain layer should be handled in a snapping operation
|
||||
*/
|
||||
@ -84,28 +75,27 @@ class QgsSnappingUtils : QObject
|
||||
QgsTolerance::UnitType unit;
|
||||
};
|
||||
|
||||
/** Set layers which will be used for snapping */
|
||||
void setLayers( const QList<QgsSnappingUtils::LayerConfig>& layers );
|
||||
/** Query layers used for snapping */
|
||||
QList<QgsSnappingUtils::LayerConfig> layers() const;
|
||||
|
||||
/** Set whether to consider intersections of nearby segments for snapping */
|
||||
void setSnapOnIntersections( bool enabled );
|
||||
/** Query whether to consider intersections of nearby segments for snapping */
|
||||
bool snapOnIntersections() const;
|
||||
|
||||
/** Get extra information about the instance
|
||||
* @note added in QGIS 2.14
|
||||
*/
|
||||
QString dump();
|
||||
|
||||
public slots:
|
||||
/** Read snapping configuration from the project */
|
||||
void readConfigFromProject();
|
||||
/**
|
||||
* The snapping configuration controls the behavior of this object
|
||||
*/
|
||||
QgsSnappingConfig config() const;
|
||||
|
||||
/**
|
||||
* The snapping configuration controls the behavior of this object
|
||||
*/
|
||||
void setConfig( const QgsSnappingConfig& snappingConfig );
|
||||
|
||||
signals:
|
||||
/** Emitted when snapping configuration has been changed
|
||||
* @note added in QGIS 2.14
|
||||
/**
|
||||
* Emitted when the snapping settings object changes.
|
||||
*/
|
||||
void configChanged();
|
||||
|
||||
|
@ -49,6 +49,8 @@ SET(QGIS_APP_SRCS
|
||||
qgsmaplayerstyleguiutils.cpp
|
||||
qgsrulebasedlabelingwidget.cpp
|
||||
qgssavestyletodbdialog.cpp
|
||||
qgssnappinglayertreemodel.cpp
|
||||
qgssnappingwidget.cpp
|
||||
qgsstatusbarcoordinateswidget.cpp
|
||||
qgsstatusbarmagnifierwidget.cpp
|
||||
qgsstatusbarscalewidget.cpp
|
||||
@ -228,6 +230,8 @@ SET (QGIS_APP_MOC_HDRS
|
||||
qgsmaplayerstyleguiutils.h
|
||||
qgsrulebasedlabelingwidget.h
|
||||
qgssavestyletodbdialog.h
|
||||
qgssnappinglayertreemodel.h
|
||||
qgssnappingwidget.h
|
||||
qgsstatusbarcoordinateswidget.h
|
||||
qgsstatusbarmagnifierwidget.h
|
||||
qgsstatusbarscalewidget.h
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <QDesktopServices>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QEvent>
|
||||
#include <QFile>
|
||||
@ -215,6 +216,7 @@
|
||||
#include "qgsshortcutsmanager.h"
|
||||
#include "qgssinglebandgrayrenderer.h"
|
||||
#include "qgssnappingdialog.h"
|
||||
#include "qgssnappingwidget.h"
|
||||
#include "qgssourceselectdialog.h"
|
||||
#include "qgssponsors.h"
|
||||
#include "qgsstatisticalsummarydockwidget.h"
|
||||
@ -589,6 +591,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
|
||||
, mInternalClipboard( nullptr )
|
||||
, mShowProjectionTab( false )
|
||||
, mPythonUtils( nullptr )
|
||||
, mSnappingWidget( nullptr )
|
||||
, mMapStylingDock( nullptr )
|
||||
, mComposerManager( nullptr )
|
||||
, mpTileScaleWidget( nullptr )
|
||||
@ -758,8 +761,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
|
||||
startProfile( "Snapping utils" );
|
||||
mSnappingUtils = new QgsMapCanvasSnappingUtils( mMapCanvas, this );
|
||||
mMapCanvas->setSnappingUtils( mSnappingUtils );
|
||||
connect( QgsProject::instance(), SIGNAL( snapSettingsChanged() ), mSnappingUtils, SLOT( readConfigFromProject() ) );
|
||||
connect( this, SIGNAL( projectRead() ), mSnappingUtils, SLOT( readConfigFromProject() ) );
|
||||
connect( QgsProject::instance(), &QgsProject::snappingConfigChanged, this, &QgisApp::onSnappingConfigChanged );
|
||||
|
||||
endProfile();
|
||||
|
||||
functionProfile( &QgisApp::createActions, this, "Create actions" );
|
||||
@ -807,8 +810,27 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
|
||||
endProfile();
|
||||
|
||||
startProfile( "Snapping dialog" );
|
||||
mSnappingDialog = new QgsSnappingDialog( this, mMapCanvas );
|
||||
mSnappingDialog->setObjectName( "SnappingOption" );
|
||||
mSnappingDialogWidget = new QgsSnappingWidget( QgsProject::instance(), mMapCanvas, this );
|
||||
QString mainSnappingWidgetMode = QSettings().value( "/qgis/mainSnappingWidgetMode", "dialog" ).toString();
|
||||
if ( mainSnappingWidgetMode == "dock" )
|
||||
{
|
||||
QgsSnappingDock* dock = new QgsSnappingDock( tr( "Snapping and Digitizing Options" ), QgisApp::instance() );
|
||||
dock->setAllowedAreas( Qt::AllDockWidgetAreas );
|
||||
dock->setWidget( mSnappingDialogWidget );
|
||||
addDockWidget( Qt::LeftDockWidgetArea, dock );
|
||||
mSnappingDialogContainer = dock;
|
||||
dock->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
QDialog* dialog = new QDialog( this );
|
||||
dialog->setWindowTitle( tr( "Project snapping settings" ) );
|
||||
QVBoxLayout* layout = new QVBoxLayout( dialog );
|
||||
QDialogButtonBox* button = new QDialogButtonBox( QDialogButtonBox::Close );
|
||||
layout->addWidget( mSnappingDialogWidget );
|
||||
layout->addWidget( button );
|
||||
mSnappingDialogContainer = dialog;
|
||||
}
|
||||
endProfile();
|
||||
|
||||
mBrowserWidget = new QgsBrowserDockWidget( tr( "Browser Panel" ), this );
|
||||
@ -1160,7 +1182,8 @@ QgisApp::QgisApp()
|
||||
, mAdvancedDigitizingDockWidget( nullptr )
|
||||
, mStatisticalSummaryDockWidget( nullptr )
|
||||
, mBookMarksDockWidget( nullptr )
|
||||
, mSnappingDialog( nullptr )
|
||||
, mSnappingWidget( nullptr )
|
||||
, mSnappingDialogContainer( nullptr )
|
||||
, mPluginManager( nullptr )
|
||||
, mMapStylingDock( nullptr )
|
||||
, mMapStyleWidget( nullptr )
|
||||
@ -2048,6 +2071,18 @@ void QgisApp::createToolBars()
|
||||
<< mWebToolBar
|
||||
<< mLabelToolBar;
|
||||
|
||||
|
||||
// snapping widget as tool bar
|
||||
QString simpleSnappingWidgetMode = QSettings().value( "/qgis/simpleSnappingWidgetMode", "toolbar" ).toString();
|
||||
if ( simpleSnappingWidgetMode != "statusbar" )
|
||||
{
|
||||
mSnappingWidget = new QgsSnappingWidget( QgsProject::instance(), mMapCanvas, mSnappingToolBar );
|
||||
mSnappingWidget->setObjectName( "mSnappingWidget" );
|
||||
//mSnappingWidget->setFont( myFont );
|
||||
connect( mSnappingWidget, SIGNAL( snapSettingsChanged() ), QgsProject::instance(), SIGNAL( snapSettingsChanged() ) );
|
||||
mSnappingToolBar->addWidget( mSnappingWidget );
|
||||
}
|
||||
|
||||
QList<QAction*> toolbarMenuActions;
|
||||
// Set action names so that they can be used in customization
|
||||
Q_FOREACH ( QToolBar *toolBar, toolbarMenuToolBars )
|
||||
@ -2395,6 +2430,17 @@ void QgisApp::createStatusBar()
|
||||
mRotationLabel->setToolTip( tr( "Current clockwise map rotation in degrees" ) );
|
||||
statusBar()->addPermanentWidget( mRotationLabel, 0 );
|
||||
|
||||
// snapping widget
|
||||
QString simpleSnappingWidgetMode = QSettings().value( "/qgis/simpleSnappingWidgetMode", "toolbar" ).toString();
|
||||
if ( simpleSnappingWidgetMode == "statusbar" )
|
||||
{
|
||||
mSnappingWidget = new QgsSnappingWidget( QgsProject::instance(), mMapCanvas, statusBar() );
|
||||
mSnappingWidget->setObjectName( "mSnappingWidget" );
|
||||
mSnappingWidget->setFont( myFont );
|
||||
connect( mSnappingWidget, SIGNAL( snapSettingsChanged() ), QgsProject::instance(), SIGNAL( snapSettingsChanged() ) );
|
||||
statusBar()->addPermanentWidget( mSnappingWidget, 0 );
|
||||
}
|
||||
|
||||
mRotationEdit = new QgsDoubleSpinBox( statusBar() );
|
||||
mRotationEdit->setObjectName( "mRotationEdit" );
|
||||
mRotationEdit->setClearValue( 0.0 );
|
||||
@ -7179,7 +7225,7 @@ void QgisApp::offsetPointSymbol()
|
||||
|
||||
void QgisApp::snappingOptions()
|
||||
{
|
||||
mSnappingDialog->show();
|
||||
mSnappingDialogContainer->show();
|
||||
}
|
||||
|
||||
void QgisApp::splitFeatures()
|
||||
@ -11211,6 +11257,11 @@ void QgisApp::onTransactionGroupsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void QgisApp::onSnappingConfigChanged()
|
||||
{
|
||||
mSnappingUtils->setConfig( QgsProject::instance()->snappingConfig() );
|
||||
}
|
||||
|
||||
void QgisApp::startProfile( const QString& name )
|
||||
{
|
||||
mProfiler->start( name );
|
||||
|
@ -40,49 +40,50 @@ class QValidator;
|
||||
class QgisAppInterface;
|
||||
class QgisAppStyleSheet;
|
||||
class QgsAnnotationItem;
|
||||
class QgsAuthManager;
|
||||
class QgsBookmarks;
|
||||
class QgsClipboard;
|
||||
class QgsComposer;
|
||||
class QgsComposerManager;
|
||||
class QgsComposerView;
|
||||
class QgsCustomDropHandler;
|
||||
class QgsStatusBarCoordinatesWidget;
|
||||
class QgsStatusBarMagnifierWidget;
|
||||
class QgsStatusBarScaleWidget;
|
||||
class QgsContrastEnhancement;
|
||||
class QgsCoordinateReferenceSystem;
|
||||
class QgsCustomDropHandler;
|
||||
class QgsCustomLayerOrderWidget;
|
||||
class QgsDockWidget;
|
||||
class QgsDoubleSpinBox;
|
||||
class QgsFeature;
|
||||
class QgsFeatureStore;
|
||||
class QgsGeometry;
|
||||
class QgsLayerTreeMapCanvasBridge;
|
||||
class QgsLayerTreeView;
|
||||
class QgsMapCanvas;
|
||||
class QgsMapLayer;
|
||||
class QgsMapLayerConfigWidgetFactory;
|
||||
class QgsMapOverviewCanvas;
|
||||
class QgsMapTip;
|
||||
class QgsMapTool;
|
||||
class QgsMapToolAdvancedDigitizing;
|
||||
class QgsMapOverviewCanvas;
|
||||
class QgsPluginLayer;
|
||||
class QgsPluginLayer;
|
||||
class QgsPluginManager;
|
||||
class QgsPoint;
|
||||
class QgsProviderRegistry;
|
||||
class QgsPythonUtils;
|
||||
class QgsRasterLayer;
|
||||
class QgsRectangle;
|
||||
class QgsRuntimeProfiler;
|
||||
class QgsSnappingUtils;
|
||||
class QgsSnappingWidget;
|
||||
class QgsStatusBarCoordinatesWidget;
|
||||
class QgsStatusBarMagnifierWidget;
|
||||
class QgsStatusBarScaleWidget;
|
||||
class QgsTransactionGroup;
|
||||
class QgsUndoWidget;
|
||||
class QgsUserInputDockWidget;
|
||||
class QgsVectorLayer;
|
||||
class QgsVectorLayerTools;
|
||||
class QgsWelcomePage;
|
||||
class QgsRasterLayer;
|
||||
class QgsPluginLayer;
|
||||
class QgsCoordinateReferenceSystem;
|
||||
class QgsFeatureStore;
|
||||
class QgsAuthManager;
|
||||
class QgsPluginManager;
|
||||
class QgsRuntimeProfiler;
|
||||
class QgsBookmarks;
|
||||
|
||||
class QDomDocument;
|
||||
class QNetworkReply;
|
||||
@ -728,6 +729,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
private slots:
|
||||
void onTransactionGroupsChanged();
|
||||
|
||||
void onSnappingConfigChanged();
|
||||
|
||||
//! validate a SRS
|
||||
void validateCrs( QgsCoordinateReferenceSystem &crs );
|
||||
|
||||
@ -1741,7 +1744,10 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
QgsStatisticalSummaryDockWidget* mStatisticalSummaryDockWidget;
|
||||
QgsBookmarks* mBookMarksDockWidget;
|
||||
|
||||
QgsSnappingDialog *mSnappingDialog;
|
||||
//! snapping widget
|
||||
QgsSnappingWidget *mSnappingWidget;
|
||||
QWidget *mSnappingDialogContainer;
|
||||
QgsSnappingWidget *mSnappingDialogWidget;
|
||||
|
||||
QgsPluginManager *mPluginManager;
|
||||
QgsDockWidget *mMapStylingDock;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "qgssnappingutils.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvertexmarker.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
#include <QGraphicsProxyWidget>
|
||||
#include <QMouseEvent>
|
||||
@ -84,24 +85,20 @@ void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent* e )
|
||||
QgsSnappingUtils* snapping = mCanvas->snappingUtils();
|
||||
|
||||
// store previous settings
|
||||
int oldType;
|
||||
double oldSearchRadius;
|
||||
QgsTolerance::UnitType oldSearchRadiusUnit;
|
||||
QgsSnappingUtils::SnapToMapMode oldMode = snapping->snapToMapMode();
|
||||
snapping->defaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit );
|
||||
|
||||
QgsSnappingConfig oldConfig = snapping->config();
|
||||
QgsSnappingConfig config = snapping->config();
|
||||
// setup new settings (temporary)
|
||||
QSettings settings;
|
||||
snapping->setSnapToMapMode( QgsSnappingUtils::SnapAllLayers );
|
||||
snapping->setDefaultSettings( QgsPointLocator::Edge,
|
||||
settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble(),
|
||||
( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt() );
|
||||
config.setMode( QgsSnappingConfig::AllLayers );
|
||||
config.setType( QgsSnappingConfig::Segment );
|
||||
config.setTolerance( settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble() );
|
||||
config.setUnits( static_cast<QgsTolerance::UnitType>( settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt() ) );
|
||||
snapping->setConfig( config );
|
||||
|
||||
QgsPointLocator::Match match = snapping->snapToMap( e->pos() );
|
||||
|
||||
// restore old settings
|
||||
snapping->setSnapToMapMode( oldMode );
|
||||
snapping->setDefaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit );
|
||||
snapping->setConfig( oldConfig );
|
||||
|
||||
if ( match.hasEdge() && match.layer() )
|
||||
{
|
||||
|
@ -625,7 +625,6 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl )
|
||||
cbxShowTips->setChecked( mSettings->value( QString( "/qgis/showTips%1" ).arg( Qgis::QGIS_VERSION_INT / 100 ), true ).toBool() );
|
||||
cbxCheckVersion->setChecked( mSettings->value( "/qgis/checkVersion", true ).toBool() );
|
||||
cbxAttributeTableDocked->setChecked( mSettings->value( "/qgis/dockAttributeTable", false ).toBool() );
|
||||
cbxSnappingOptionsDocked->setChecked( mSettings->value( "/qgis/dockSnapping", false ).toBool() );
|
||||
cbxAddPostgisDC->setChecked( mSettings->value( "/qgis/addPostgisDC", false ).toBool() );
|
||||
cbxAddOracleDC->setChecked( mSettings->value( "/qgis/addOracleDC", false ).toBool() );
|
||||
cbxCompileExpressions->setChecked( mSettings->value( "/qgis/compileExpressions", true ).toBool() );
|
||||
@ -912,6 +911,15 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl )
|
||||
chkDisableAttributeValuesDlg->setChecked( mSettings->value( "/qgis/digitizing/disable_enter_attribute_values_dialog", false ).toBool() );
|
||||
mValidateGeometries->setCurrentIndex( mSettings->value( "/qgis/digitizing/validate_geometries", 1 ).toInt() );
|
||||
|
||||
mSnappingMainDialogComboBox->clear();
|
||||
mSnappingMainDialogComboBox->addItem( tr( "dialog" ), "dialog" );
|
||||
mSnappingMainDialogComboBox->addItem( tr( "dock" ), "dock" );
|
||||
mSnappingMainDialogComboBox->setCurrentIndex( mSnappingMainDialogComboBox->findData( mSettings->value( "/qgis/mainSnappingWidgetMode", "dialog" ).toString() ) );
|
||||
mSnappingSimplePanelComboBox->clear();
|
||||
mSnappingSimplePanelComboBox->addItem( tr( "tool bar" ), "toolbar" );
|
||||
mSnappingSimplePanelComboBox->addItem( tr( "status bar" ), "statusbar" );
|
||||
mSnappingSimplePanelComboBox->setCurrentIndex( mSnappingSimplePanelComboBox->findData( mSettings->value( "/qgis/simpleSnappingWidgetMode", "toolbar" ).toString() ) );
|
||||
|
||||
mOffsetJoinStyleComboBox->addItem( tr( "Round" ), 0 );
|
||||
mOffsetJoinStyleComboBox->addItem( tr( "Mitre" ), 1 );
|
||||
mOffsetJoinStyleComboBox->addItem( tr( "Bevel" ), 2 );
|
||||
@ -1182,7 +1190,9 @@ void QgsOptions::saveOptions()
|
||||
mSettings->setValue( "/qgis/scanZipInBrowser2",
|
||||
cmbScanZipInBrowser->currentData().toString() );
|
||||
mSettings->setValue( "/qgis/ignoreShapeEncoding", cbxIgnoreShapeEncoding->isChecked() );
|
||||
mSettings->setValue( "/qgis/dockSnapping", cbxSnappingOptionsDocked->isChecked() );
|
||||
mSettings->setValue( "/qgis/mainSnappingWidgetMode", mSnappingMainDialogComboBox->currentData() );
|
||||
mSettings->setValue( "/qgis/simpleSnappingWidgetMode", mSnappingSimplePanelComboBox->currentData() );
|
||||
|
||||
mSettings->setValue( "/qgis/addPostgisDC", cbxAddPostgisDC->isChecked() );
|
||||
mSettings->setValue( "/qgis/addOracleDC", cbxAddOracleDC->isChecked() );
|
||||
mSettings->setValue( "/qgis/compileExpressions", cbxCompileExpressions->isChecked() );
|
||||
|
@ -15,22 +15,24 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgssnappingdialog.h"
|
||||
#include "qgisapp.h"
|
||||
#include "qgsdockwidget.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsmaplayer.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsmaplayerregistry.h"
|
||||
#include "qgisapp.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsdockwidget.h"
|
||||
#include "qgssnappingdialog.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDoubleValidator>
|
||||
#include <QComboBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QDoubleValidator>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QDoubleSpinBox>
|
||||
|
||||
|
||||
QgsSnappingDialog::QgsSnappingDialog( QWidget* parent, QgsMapCanvas* canvas )
|
||||
: QDialog( parent )
|
||||
|
636
src/app/qgssnappinglayertreemodel.cpp
Normal file
@ -0,0 +1,636 @@
|
||||
/***************************************************************************
|
||||
qgssnappinglayertreemodel.cpp - QgsSnappingLayerTreeView
|
||||
|
||||
---------------------
|
||||
begin : 31.8.2016
|
||||
copyright : (C) 2016 by Denis Rouzaud
|
||||
email : denis.rouzaud@gmail.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDoubleSpinBox>
|
||||
|
||||
#include "qgssnappinglayertreemodel.h"
|
||||
|
||||
#include "qgslayertree.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
|
||||
|
||||
QgsSnappingLayerDelegate::QgsSnappingLayerDelegate( QgsMapCanvas* canvas, QObject* parent )
|
||||
: QItemDelegate( parent )
|
||||
, mCanvas( canvas )
|
||||
{
|
||||
}
|
||||
|
||||
QWidget* QgsSnappingLayerDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
|
||||
{
|
||||
Q_UNUSED( option );
|
||||
Q_UNUSED( index );
|
||||
|
||||
if ( index.column() == QgsSnappingLayerTreeModel::TypeColumn )
|
||||
{
|
||||
QComboBox* w = new QComboBox( parent );
|
||||
w->addItem( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingVertex.svg" ) ), "Vertex", QgsSnappingConfig::Vertex );
|
||||
w->addItem( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingVertexAndSegment.svg" ) ), "Vertex and segment", QgsSnappingConfig::VertexAndSegment );
|
||||
w->addItem( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingSegment.svg" ) ), "Segment", QgsSnappingConfig::Segment );
|
||||
return w;
|
||||
}
|
||||
|
||||
if ( index.column() == QgsSnappingLayerTreeModel::ToleranceColumn )
|
||||
{
|
||||
QDoubleSpinBox* w = new QDoubleSpinBox( parent );
|
||||
QVariant val = index.model()->data( index.model()->sibling( index.row(), QgsSnappingLayerTreeModel::UnitsColumn, index ), Qt::UserRole );
|
||||
if ( val.isValid() )
|
||||
{
|
||||
QgsTolerance::UnitType units = ( QgsTolerance::UnitType )val.toInt();
|
||||
if ( units == QgsTolerance::Pixels )
|
||||
{
|
||||
w->setDecimals( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsUnitTypes::DistanceUnitType type = QgsUnitTypes::unitType( mCanvas->mapUnits() );
|
||||
w->setDecimals( type == QgsUnitTypes::Standard ? 2 : 5 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
w->setDecimals( 5 );
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
if ( index.column() == QgsSnappingLayerTreeModel::UnitsColumn )
|
||||
{
|
||||
QComboBox* w = new QComboBox( parent );
|
||||
w->addItem( tr( "px" ), QgsTolerance::Pixels );
|
||||
w->addItem( QgsUnitTypes::toString( QgsProject::instance()->distanceUnits() ), QgsTolerance::ProjectUnits );
|
||||
return w;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QgsSnappingLayerDelegate::setEditorData( QWidget* editor, const QModelIndex& index ) const
|
||||
{
|
||||
QVariant val = index.model()->data( index, Qt::UserRole );
|
||||
if ( !val.isValid() )
|
||||
return;
|
||||
|
||||
if ( index.column() == QgsSnappingLayerTreeModel::TypeColumn )
|
||||
{
|
||||
QgsSnappingConfig::SnappingType type = ( QgsSnappingConfig::SnappingType )val.toInt();
|
||||
QComboBox *cb = qobject_cast<QComboBox *>( editor );
|
||||
if ( cb )
|
||||
{
|
||||
cb->setCurrentIndex( cb->findData( type ) );
|
||||
}
|
||||
}
|
||||
else if ( index.column() == QgsSnappingLayerTreeModel::ToleranceColumn )
|
||||
{
|
||||
QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( editor );
|
||||
if ( w )
|
||||
{
|
||||
w->setValue( val.toDouble() );
|
||||
}
|
||||
}
|
||||
else if ( index.column() == QgsSnappingLayerTreeModel::UnitsColumn )
|
||||
{
|
||||
QgsTolerance::UnitType units = ( QgsTolerance::UnitType )val.toInt();
|
||||
QComboBox *w = qobject_cast<QComboBox *>( editor );
|
||||
if ( w )
|
||||
{
|
||||
w->setCurrentIndex( w->findData( units ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingLayerDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const
|
||||
{
|
||||
if ( index.column() == QgsSnappingLayerTreeModel::TypeColumn ||
|
||||
index.column() == QgsSnappingLayerTreeModel::UnitsColumn )
|
||||
{
|
||||
QComboBox *w = qobject_cast<QComboBox *>( editor );
|
||||
if ( w )
|
||||
{
|
||||
model->setData( index, w->currentData(), Qt::EditRole );
|
||||
}
|
||||
}
|
||||
else if ( index.column() == QgsSnappingLayerTreeModel::ToleranceColumn )
|
||||
{
|
||||
QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( editor );
|
||||
if ( w )
|
||||
{
|
||||
model->setData( index, w->value(), Qt::EditRole );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QgsSnappingLayerTreeModel::QgsSnappingLayerTreeModel( QgsProject* project, QObject *parent )
|
||||
: QSortFilterProxyModel( parent )
|
||||
, mProject( project )
|
||||
, mIndividualLayerSettings( project->snappingConfig().individualLayerSettings() )
|
||||
{
|
||||
connect( project, &QgsProject::snappingConfigChanged, this, &QgsSnappingLayerTreeModel::onSnappingSettingsChanged );
|
||||
}
|
||||
|
||||
QgsSnappingLayerTreeModel::~QgsSnappingLayerTreeModel()
|
||||
{
|
||||
}
|
||||
|
||||
int QgsSnappingLayerTreeModel::columnCount( const QModelIndex& parent ) const
|
||||
{
|
||||
Q_UNUSED( parent );
|
||||
return 5;
|
||||
}
|
||||
|
||||
Qt::ItemFlags QgsSnappingLayerTreeModel::flags( const QModelIndex& idx ) const
|
||||
{
|
||||
if ( idx.column() == LayerColumn )
|
||||
{
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
|
||||
}
|
||||
|
||||
QgsVectorLayer* vl = vectorLayer( idx );
|
||||
if ( !vl )
|
||||
{
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
else
|
||||
{
|
||||
const QModelIndex layerIndex = sibling( idx.row(), LayerColumn, idx );
|
||||
if ( data( layerIndex, Qt::CheckStateRole ) == Qt::Checked )
|
||||
{
|
||||
if ( idx.column() == AvoidIntersectionColumn )
|
||||
{
|
||||
if ( vl->geometryType() == QgsWkbTypes::PolygonGeometry )
|
||||
{
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Qt::NoItemFlags;
|
||||
}
|
||||
|
||||
QModelIndex QgsSnappingLayerTreeModel::index( int row, int column, const QModelIndex& parent ) const
|
||||
{
|
||||
QModelIndex newIndex = QSortFilterProxyModel::index( row, 0, parent );
|
||||
if ( column == LayerColumn )
|
||||
return newIndex;
|
||||
|
||||
return createIndex( row, column, newIndex.internalId() );
|
||||
}
|
||||
|
||||
QModelIndex QgsSnappingLayerTreeModel::parent( const QModelIndex& child ) const
|
||||
{
|
||||
return QSortFilterProxyModel::parent( createIndex( child.row(), 0, child.internalId() ) );
|
||||
}
|
||||
|
||||
QModelIndex QgsSnappingLayerTreeModel::sibling( int row, int column, const QModelIndex& idx ) const
|
||||
{
|
||||
QModelIndex parent = idx.parent();
|
||||
return index( row, column, parent );
|
||||
}
|
||||
|
||||
QgsVectorLayer* QgsSnappingLayerTreeModel::vectorLayer( const QModelIndex& idx ) const
|
||||
{
|
||||
QgsLayerTreeNode* node;
|
||||
if ( idx.column() == LayerColumn )
|
||||
{
|
||||
node = mLayerTreeModel->index2node( mapToSource( idx ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
node = mLayerTreeModel->index2node( mapToSource( index( idx.row(), 0, idx.parent() ) ) );
|
||||
}
|
||||
|
||||
if ( !node || !QgsLayerTree::isLayer( node ) )
|
||||
return nullptr;
|
||||
|
||||
return qobject_cast<QgsVectorLayer*>( QgsLayerTree::toLayer( node )->layer() );
|
||||
}
|
||||
|
||||
void QgsSnappingLayerTreeModel::onSnappingSettingsChanged()
|
||||
{
|
||||
const QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> oldSettings = mIndividualLayerSettings;
|
||||
|
||||
Q_FOREACH ( QgsVectorLayer* vl, oldSettings.keys() )
|
||||
{
|
||||
if ( !mProject->snappingConfig().individualLayerSettings().contains( vl ) )
|
||||
{
|
||||
beginResetModel();
|
||||
mIndividualLayerSettings = mProject->snappingConfig().individualLayerSettings();
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
Q_FOREACH ( QgsVectorLayer* vl, mProject->snappingConfig().individualLayerSettings().keys() )
|
||||
{
|
||||
if ( !oldSettings.contains( vl ) )
|
||||
{
|
||||
beginResetModel();
|
||||
mIndividualLayerSettings = mProject->snappingConfig().individualLayerSettings();
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hasRowchanged( mLayerTreeModel->rootGroup(), oldSettings );
|
||||
}
|
||||
|
||||
void QgsSnappingLayerTreeModel::hasRowchanged( QgsLayerTreeNode* node, const QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> &oldSettings )
|
||||
{
|
||||
if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
|
||||
{
|
||||
Q_FOREACH ( QgsLayerTreeNode *child, node->children() )
|
||||
{
|
||||
hasRowchanged( child, oldSettings );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QModelIndex idx = mapFromSource( mLayerTreeModel->node2index( node ) );
|
||||
QgsVectorLayer* vl = vectorLayer( idx );
|
||||
if ( !vl )
|
||||
{
|
||||
emit dataChanged( QModelIndex(), idx );
|
||||
}
|
||||
if ( oldSettings.value( vl ) != mProject->snappingConfig().individualLayerSettings().value( vl ) )
|
||||
{
|
||||
mIndividualLayerSettings.insert( vl, mProject->snappingConfig().individualLayerSettings().value( vl ) );
|
||||
emit dataChanged( idx, index( idx.row(), columnCount( idx ) - 1 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QgsLayerTreeModel* QgsSnappingLayerTreeModel::layerTreeModel() const
|
||||
{
|
||||
return mLayerTreeModel;
|
||||
}
|
||||
|
||||
void QgsSnappingLayerTreeModel::setLayerTreeModel( QgsLayerTreeModel* layerTreeModel )
|
||||
{
|
||||
mLayerTreeModel = layerTreeModel;
|
||||
QSortFilterProxyModel::setSourceModel( layerTreeModel );
|
||||
}
|
||||
|
||||
bool QgsSnappingLayerTreeModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const
|
||||
{
|
||||
QgsLayerTreeNode* node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
|
||||
return nodeShown( node );
|
||||
}
|
||||
|
||||
bool QgsSnappingLayerTreeModel::nodeShown( QgsLayerTreeNode* node ) const
|
||||
{
|
||||
if ( !node )
|
||||
return false;
|
||||
if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
|
||||
{
|
||||
Q_FOREACH ( QgsLayerTreeNode *child, node->children() )
|
||||
{
|
||||
if ( nodeShown( child ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer();
|
||||
if ( layer && layer->type() == QgsMapLayer::VectorLayer )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant QgsSnappingLayerTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
|
||||
{
|
||||
if ( orientation == Qt::Horizontal )
|
||||
{
|
||||
if ( role == Qt::DisplayRole )
|
||||
{
|
||||
switch ( section )
|
||||
{
|
||||
case 0:
|
||||
return tr( "Layer" );
|
||||
case 1:
|
||||
return tr( "Type" );
|
||||
case 2:
|
||||
return tr( "Tolerance" );
|
||||
case 3:
|
||||
return tr( "Units" );
|
||||
case 4:
|
||||
return tr( "Avoid intersection" );
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mLayerTreeModel->headerData( section, orientation, role );
|
||||
}
|
||||
|
||||
QVariant QgsSnappingLayerTreeModel::data( const QModelIndex& idx, int role ) const
|
||||
{
|
||||
if ( idx.column() == LayerColumn )
|
||||
{
|
||||
if ( role == Qt::CheckStateRole )
|
||||
{
|
||||
QgsVectorLayer *vl = vectorLayer( idx );
|
||||
if ( vl && mIndividualLayerSettings.contains( vl ) )
|
||||
{
|
||||
const QgsSnappingConfig::IndividualLayerSettings ls = mIndividualLayerSettings.value( vl );
|
||||
if ( !ls.valid() )
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
if ( ls.enabled() )
|
||||
{
|
||||
return Qt::Checked;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// i.e. this is a group, analyse its children
|
||||
bool hasChecked = false, hasUnchecked = false;
|
||||
int n;
|
||||
for ( n = 0; !hasChecked || !hasUnchecked; n++ )
|
||||
{
|
||||
QVariant v = data( idx.child( n, 0 ), role );
|
||||
if ( !v.isValid() )
|
||||
break;
|
||||
|
||||
switch ( v.toInt() )
|
||||
{
|
||||
case Qt::PartiallyChecked:
|
||||
// parent of partially checked child shared state
|
||||
return Qt::PartiallyChecked;
|
||||
|
||||
case Qt::Checked:
|
||||
hasChecked = true;
|
||||
break;
|
||||
|
||||
case Qt::Unchecked:
|
||||
hasUnchecked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// unchecked leaf
|
||||
if ( n == 0 )
|
||||
return Qt::Unchecked;
|
||||
|
||||
// both
|
||||
if ( hasChecked && hasUnchecked )
|
||||
return Qt::PartiallyChecked;
|
||||
|
||||
if ( hasChecked )
|
||||
return Qt::Checked;
|
||||
|
||||
Q_ASSERT( hasUnchecked );
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return mLayerTreeModel->data( mapToSource( idx ), role );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsVectorLayer *vl = vectorLayer( idx );
|
||||
|
||||
if ( !vl || !mIndividualLayerSettings.contains( vl ) )
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const QgsSnappingConfig::IndividualLayerSettings ls = mIndividualLayerSettings.value( vl );
|
||||
|
||||
// type
|
||||
if ( idx.column() == TypeColumn )
|
||||
{
|
||||
if ( role == Qt::DisplayRole )
|
||||
{
|
||||
switch ( ls.type() )
|
||||
{
|
||||
case QgsSnappingConfig::Vertex:
|
||||
return tr( "vertex" );
|
||||
case QgsSnappingConfig::VertexAndSegment:
|
||||
return tr( "vertex and segment" );
|
||||
case QgsSnappingConfig::Segment:
|
||||
return tr( "segment" );
|
||||
default:
|
||||
return tr( "N/A" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( role == Qt::UserRole )
|
||||
return ls.type();
|
||||
}
|
||||
|
||||
// tolerance
|
||||
if ( idx.column() == ToleranceColumn )
|
||||
{
|
||||
if ( role == Qt::DisplayRole )
|
||||
{
|
||||
return QString::number( ls.tolerance() );
|
||||
}
|
||||
|
||||
if ( role == Qt::UserRole )
|
||||
{
|
||||
return ls.tolerance();
|
||||
}
|
||||
}
|
||||
|
||||
// units
|
||||
if ( idx.column() == UnitsColumn )
|
||||
{
|
||||
if ( role == Qt::DisplayRole )
|
||||
{
|
||||
switch ( ls.units() )
|
||||
{
|
||||
case QgsTolerance::Pixels:
|
||||
return tr( "pixels" );
|
||||
case QgsTolerance::ProjectUnits:
|
||||
return QgsUnitTypes::toString( QgsProject::instance()->distanceUnits() );
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
if ( role == Qt::UserRole )
|
||||
{
|
||||
return ls.units();
|
||||
}
|
||||
}
|
||||
|
||||
// avoid intersection
|
||||
if ( idx.column() == AvoidIntersectionColumn )
|
||||
{
|
||||
if ( role == Qt::CheckStateRole && vl->geometryType() == QgsWkbTypes::PolygonGeometry )
|
||||
{
|
||||
if ( ls.avoidIntersection() )
|
||||
{
|
||||
return Qt::Checked;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool QgsSnappingLayerTreeModel::setData( const QModelIndex& index, const QVariant& value, int role )
|
||||
{
|
||||
if ( index.column() == LayerColumn )
|
||||
{
|
||||
if ( role == Qt::CheckStateRole )
|
||||
{
|
||||
int i = 0;
|
||||
for ( i = 0; ; i++ )
|
||||
{
|
||||
QModelIndex child = index.child( i, 0 );
|
||||
if ( !child.isValid() )
|
||||
break;
|
||||
|
||||
setData( child, value, role );
|
||||
}
|
||||
|
||||
if ( i == 0 )
|
||||
{
|
||||
QgsVectorLayer* vl = vectorLayer( index );
|
||||
if ( !vl || !mIndividualLayerSettings.contains( vl ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QgsSnappingConfig::IndividualLayerSettings ls = mIndividualLayerSettings.value( vl );
|
||||
if ( !ls.valid() )
|
||||
return false;
|
||||
if ( value.toInt() == Qt::Checked )
|
||||
ls.setEnabled( true );
|
||||
else if ( value.toInt() == Qt::Unchecked )
|
||||
ls.setEnabled( false );
|
||||
else
|
||||
Q_ASSERT( "expected checked or unchecked" );
|
||||
|
||||
QgsSnappingConfig config = mProject->snappingConfig();
|
||||
config.setIndividualLayerSettings( vl, ls );
|
||||
mProject->setSnappingConfig( config );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return mLayerTreeModel->setData( mapToSource( index ), value, role );
|
||||
}
|
||||
|
||||
if ( index.column() == TypeColumn && role == Qt::EditRole )
|
||||
{
|
||||
QgsVectorLayer *vl = vectorLayer( index );
|
||||
if ( vl )
|
||||
{
|
||||
if ( !mIndividualLayerSettings.contains( vl ) )
|
||||
return false;
|
||||
|
||||
QgsSnappingConfig::IndividualLayerSettings ls = mIndividualLayerSettings.value( vl );
|
||||
if ( !ls.valid() )
|
||||
return false;
|
||||
|
||||
ls.setType(( QgsSnappingConfig::SnappingType )value.toInt() );
|
||||
QgsSnappingConfig config = mProject->snappingConfig();
|
||||
config.setIndividualLayerSettings( vl, ls );
|
||||
mProject->setSnappingConfig( config );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( index.column() == ToleranceColumn && role == Qt::EditRole )
|
||||
{
|
||||
QgsVectorLayer *vl = vectorLayer( index );
|
||||
if ( vl )
|
||||
{
|
||||
if ( !mIndividualLayerSettings.contains( vl ) )
|
||||
return false;
|
||||
|
||||
QgsSnappingConfig::IndividualLayerSettings ls = mIndividualLayerSettings.value( vl );
|
||||
if ( !ls.valid() )
|
||||
return false;
|
||||
|
||||
ls.setTolerance( value.toDouble() );
|
||||
QgsSnappingConfig config = mProject->snappingConfig();
|
||||
config.setIndividualLayerSettings( vl, ls );
|
||||
mProject->setSnappingConfig( config );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( index.column() == UnitsColumn && role == Qt::EditRole )
|
||||
{
|
||||
QgsVectorLayer *vl = vectorLayer( index );
|
||||
if ( vl )
|
||||
{
|
||||
if ( !mIndividualLayerSettings.contains( vl ) )
|
||||
return false;
|
||||
|
||||
QgsSnappingConfig::IndividualLayerSettings ls = mIndividualLayerSettings.value( vl );
|
||||
if ( !ls.valid() )
|
||||
return false;
|
||||
|
||||
ls.setUnits(( QgsTolerance::UnitType )value.toInt() );
|
||||
QgsSnappingConfig config = mProject->snappingConfig();
|
||||
config.setIndividualLayerSettings( vl, ls );
|
||||
mProject->setSnappingConfig( config );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( index.column() == AvoidIntersectionColumn && role == Qt::CheckStateRole )
|
||||
{
|
||||
QgsVectorLayer *vl = vectorLayer( index );
|
||||
if ( vl )
|
||||
{
|
||||
if ( !mIndividualLayerSettings.contains( vl ) )
|
||||
return false;
|
||||
|
||||
QgsSnappingConfig::IndividualLayerSettings ls = mIndividualLayerSettings.value( vl );
|
||||
if ( !ls.valid() )
|
||||
return false;
|
||||
|
||||
ls.setAvoidIntersection( value.toInt() == Qt::Checked );
|
||||
QgsSnappingConfig config = mProject->snappingConfig();
|
||||
config.setIndividualLayerSettings( vl, ls );
|
||||
mProject->setSnappingConfig( config );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
94
src/app/qgssnappinglayertreemodel.h
Normal file
@ -0,0 +1,94 @@
|
||||
/***************************************************************************
|
||||
qgssnappinglayertreemodel.h - QgsSnappingLayerTreeModel
|
||||
|
||||
---------------------
|
||||
begin : 31.8.2016
|
||||
copyright : (C) 2016 by Denis Rouzaud
|
||||
email : denis.rouzaud@gmail.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSSNAPPINGLAYERTREEVIEW_H
|
||||
#define QGSSNAPPINGLAYERTREEVIEW_H
|
||||
|
||||
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QItemDelegate>
|
||||
|
||||
#include "qgslayertreemodel.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
class QgsMapCanvas;
|
||||
class QgsProject;
|
||||
|
||||
|
||||
class APP_EXPORT QgsSnappingLayerDelegate : public QItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QgsSnappingLayerDelegate( QgsMapCanvas* canvas, QObject *parent = nullptr );
|
||||
|
||||
QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
|
||||
void setEditorData( QWidget *editor, const QModelIndex &index ) const override;
|
||||
void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const override;
|
||||
|
||||
private:
|
||||
QgsMapCanvas* mCanvas;
|
||||
};
|
||||
|
||||
|
||||
class APP_EXPORT QgsSnappingLayerTreeModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Columns
|
||||
{
|
||||
LayerColumn = 0,
|
||||
TypeColumn,
|
||||
ToleranceColumn,
|
||||
UnitsColumn,
|
||||
AvoidIntersectionColumn
|
||||
};
|
||||
|
||||
QgsSnappingLayerTreeModel( QgsProject* project, QObject* parent = nullptr );
|
||||
~QgsSnappingLayerTreeModel();
|
||||
|
||||
int columnCount( const QModelIndex& parent ) const override;
|
||||
QVariant headerData( int section, Qt::Orientation orientation, int role ) const override;
|
||||
Qt::ItemFlags flags( const QModelIndex& idx ) const override;
|
||||
QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const override;
|
||||
QModelIndex parent( const QModelIndex& child ) const override;
|
||||
QModelIndex sibling( int row, int column, const QModelIndex &idx ) const override;
|
||||
QVariant data( const QModelIndex& index, int role ) const override;
|
||||
bool setData( const QModelIndex& index, const QVariant& value, int role ) override;
|
||||
|
||||
QgsLayerTreeModel* layerTreeModel() const;
|
||||
void setLayerTreeModel( QgsLayerTreeModel* layerTreeModel );
|
||||
|
||||
QgsVectorLayer* vectorLayer( const QModelIndex& idx ) const;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override;
|
||||
|
||||
private slots:
|
||||
void onSnappingSettingsChanged();
|
||||
|
||||
private:
|
||||
bool nodeShown( QgsLayerTreeNode* node ) const;
|
||||
|
||||
QgsProject* mProject;
|
||||
QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> mIndividualLayerSettings;
|
||||
QgsLayerTreeModel* mLayerTreeModel;
|
||||
|
||||
void hasRowchanged( QgsLayerTreeNode* node, const QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings>& oldSettings );
|
||||
};
|
||||
|
||||
#endif // QGSSNAPPINGLAYERTREEVIEW_H
|
476
src/app/qgssnappingwidget.cpp
Normal file
@ -0,0 +1,476 @@
|
||||
/***************************************************************************
|
||||
qgssnappingwidget.cpp
|
||||
begin : August 2016
|
||||
copyright : (C) 2016 Denis Rouzaud
|
||||
email : denis.rouzaud@gmail.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <QAction>
|
||||
#include <QComboBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFont>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QSettings>
|
||||
#include <QStatusBar>
|
||||
#include <QToolBar>
|
||||
#include <QToolButton>
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgslayertreegroup.h"
|
||||
#include "qgslayertree.h"
|
||||
#include "qgslayertreeview.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsmaplayer.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
#include "qgssnappinglayertreemodel.h"
|
||||
#include "qgssnappingwidget.h"
|
||||
#include "qgsunittypes.h"
|
||||
|
||||
|
||||
|
||||
QgsSnappingWidget::QgsSnappingWidget( QgsProject* project, QgsMapCanvas *canvas, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, mProject( project )
|
||||
, mCanvas( canvas )
|
||||
, mModeAction( nullptr )
|
||||
, mTypeAction( nullptr )
|
||||
, mToleranceAction( nullptr )
|
||||
, mUnitAction( nullptr )
|
||||
, mLayerTreeView( nullptr )
|
||||
{
|
||||
// detect the type of display
|
||||
QToolBar* tb = qobject_cast<QToolBar*>( parent );
|
||||
if ( tb )
|
||||
{
|
||||
mDisplayMode = ToolBar;
|
||||
setObjectName( "SnappingOptionToolBar" );
|
||||
}
|
||||
else
|
||||
{
|
||||
QStatusBar *sb = qobject_cast<QStatusBar*>( parent );
|
||||
if ( sb )
|
||||
{
|
||||
mDisplayMode = StatusBar;
|
||||
setObjectName( "SnappingOptionStatusBar" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDisplayMode = Widget;
|
||||
setObjectName( "SnappingOptionDialog" );
|
||||
}
|
||||
}
|
||||
|
||||
// enable button
|
||||
mEnabledAction = new QAction( this );
|
||||
mEnabledAction->setCheckable( true );
|
||||
mEnabledAction->setIcon( QIcon( QgsApplication::getThemeIcon( "/mIconSnapping.svg" ) ) );
|
||||
mEnabledAction->setToolTip( tr( "Enable snapping" ) );
|
||||
mEnabledAction->setObjectName( "EnableSnappingAction" );
|
||||
connect( mEnabledAction, SIGNAL( toggled( bool ) ) , this, SLOT( enableSnapping( bool ) ) );
|
||||
|
||||
// mode button
|
||||
mModeButton = new QToolButton();
|
||||
mModeButton->setToolTip( tr( "Snapping mode" ) );
|
||||
mModeButton->setPopupMode( QToolButton::InstantPopup );
|
||||
QMenu *modeMenu = new QMenu( tr( "Set snapping mode" ), this );
|
||||
mAllLayersAction = new QAction( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingAllLayers.svg" ) ), "All layers", modeMenu );
|
||||
mActiveLayerAction = new QAction( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingActiveLayer.svg" ) ), "Active layer", modeMenu );
|
||||
mAdvancedModeAction = new QAction( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingAdvanced.svg" ) ), "Advanced configuration", modeMenu );
|
||||
modeMenu->addAction( mAllLayersAction );
|
||||
modeMenu->addAction( mActiveLayerAction );
|
||||
modeMenu->addAction( mAdvancedModeAction );
|
||||
mModeButton->setMenu( modeMenu );
|
||||
mModeButton->setObjectName( "SnappingModeButton" );
|
||||
if ( mDisplayMode == Widget )
|
||||
{
|
||||
mModeButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
|
||||
}
|
||||
connect( mModeButton, SIGNAL( triggered( QAction* ) ), this, SLOT( modeButtonTriggered( QAction* ) ) );
|
||||
|
||||
// type button
|
||||
mTypeButton = new QToolButton();
|
||||
mTypeButton->setToolTip( tr( "Snapping type" ) );
|
||||
mTypeButton->setPopupMode( QToolButton::InstantPopup );
|
||||
QMenu *typeMenu = new QMenu( tr( "Set snapping mode" ), this );
|
||||
mVertexAction = new QAction( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingVertex.svg" ) ), "Vertex", typeMenu );
|
||||
mVertexAndSegmentAction = new QAction( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingVertexAndSegment.svg" ) ), "Vertex and segment", typeMenu );
|
||||
mSegmentAction = new QAction( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingSegment.svg" ) ), "Segment", typeMenu );
|
||||
typeMenu->addAction( mVertexAction );
|
||||
typeMenu->addAction( mVertexAndSegmentAction );
|
||||
typeMenu->addAction( mSegmentAction );
|
||||
mTypeButton->setMenu( typeMenu );
|
||||
mTypeButton->setObjectName( "SnappingTypeButton" );
|
||||
if ( mDisplayMode == Widget )
|
||||
{
|
||||
mTypeButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
|
||||
}
|
||||
connect( mTypeButton, SIGNAL( triggered( QAction* ) ), this, SLOT( typeButtonTriggered( QAction* ) ) );
|
||||
|
||||
// tolerance
|
||||
mToleranceSpinBox = new QDoubleSpinBox();
|
||||
mToleranceSpinBox->setToolTip( tr( "Snapping tolerance in defined units" ) );
|
||||
mToleranceSpinBox->setObjectName( "SnappingToleranceSpinBox" );
|
||||
connect( mToleranceSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( changeTolerance( double ) ) );
|
||||
|
||||
// units
|
||||
mUnitsComboBox = new QComboBox();
|
||||
mUnitsComboBox->addItem( tr( "px" ), QgsTolerance::Pixels );
|
||||
mUnitsComboBox->addItem( QgsUnitTypes::toString( QgsProject::instance()->distanceUnits() ), QgsTolerance::ProjectUnits );
|
||||
mUnitsComboBox->setToolTip( tr( "Snapping unit type: pixels (px) or map units (mu)" ) );
|
||||
mUnitsComboBox->setObjectName( "SnappingUnitComboBox" );
|
||||
connect( mUnitsComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( changeUnit( int ) ) );
|
||||
|
||||
// topological editing button
|
||||
mTopologicalEditingAction = new QAction( tr( "topological editing" ), this );
|
||||
mTopologicalEditingAction->setCheckable( true );
|
||||
mTopologicalEditingAction->setIcon( QIcon( QgsApplication::getThemeIcon( "/mIconTopologicalEditing.svg" ) ) );
|
||||
mTopologicalEditingAction->setToolTip( tr( "Enable topological editing" ) );
|
||||
mTopologicalEditingAction->setObjectName( "TopologicalEditingAction" );
|
||||
connect( mTopologicalEditingAction, SIGNAL( toggled( bool ) ) , this, SLOT( enableTopologicalEditing( bool ) ) );
|
||||
|
||||
// snapping on intersection button
|
||||
mIntersectionSnappingAction = new QAction( tr( "snapping on intersection" ), this );
|
||||
mIntersectionSnappingAction->setCheckable( true );
|
||||
mIntersectionSnappingAction->setIcon( QIcon( QgsApplication::getThemeIcon( "/mIconSnappingIntersection.svg" ) ) );
|
||||
mIntersectionSnappingAction->setToolTip( tr( "Enable snapping on intersection" ) );
|
||||
mIntersectionSnappingAction->setObjectName( "IntersectionSnappingAction" );
|
||||
connect( mIntersectionSnappingAction, SIGNAL( toggled( bool ) ) , this, SLOT( enableIntersectionSnapping( bool ) ) );
|
||||
|
||||
// layout
|
||||
if ( mDisplayMode == ToolBar )
|
||||
{
|
||||
// hiding widget in a toolbar is not possible, actions are required
|
||||
tb->addAction( mEnabledAction );
|
||||
mModeAction = tb->addWidget( mModeButton );
|
||||
mTypeAction = tb->addWidget( mTypeButton );
|
||||
mToleranceAction = tb->addWidget( mToleranceSpinBox );
|
||||
mUnitAction = tb->addWidget( mUnitsComboBox );
|
||||
tb->addAction( mTopologicalEditingAction );
|
||||
tb->addAction( mIntersectionSnappingAction );
|
||||
}
|
||||
else
|
||||
{
|
||||
// mode = widget or status bar
|
||||
QHBoxLayout* layout = new QHBoxLayout();
|
||||
|
||||
QToolButton* enabledButton = new QToolButton();
|
||||
enabledButton->addAction( mEnabledAction );
|
||||
enabledButton->setDefaultAction( mEnabledAction );
|
||||
layout->addWidget( enabledButton );
|
||||
|
||||
layout->addWidget( mModeButton );
|
||||
layout->addWidget( mTypeButton );
|
||||
layout->addWidget( mToleranceSpinBox ) ;
|
||||
layout->addWidget( mUnitsComboBox ) ;
|
||||
|
||||
QToolButton* topoButton = new QToolButton();
|
||||
topoButton->addAction( mTopologicalEditingAction );
|
||||
topoButton->setDefaultAction( mTopologicalEditingAction );
|
||||
if ( mDisplayMode == Widget )
|
||||
{
|
||||
topoButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
|
||||
}
|
||||
layout->addWidget( topoButton );
|
||||
QToolButton* interButton = new QToolButton();
|
||||
interButton->addAction( mIntersectionSnappingAction );
|
||||
interButton->setDefaultAction( mIntersectionSnappingAction );
|
||||
if ( mDisplayMode == Widget )
|
||||
{
|
||||
interButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
|
||||
}
|
||||
layout->addWidget( interButton );
|
||||
|
||||
layout->setContentsMargins( 0, 0, 0, 0 );
|
||||
layout->setAlignment( Qt::AlignRight );
|
||||
layout->setSpacing( mDisplayMode == Widget ? 3 : 0 );
|
||||
|
||||
if ( mDisplayMode == Widget )
|
||||
{
|
||||
mLayerTreeView = new QTreeView();
|
||||
QgsSnappingLayerTreeModel* model = new QgsSnappingLayerTreeModel( mProject, this );
|
||||
model->setLayerTreeModel( new QgsLayerTreeModel( QgsProject::instance()->layerTreeRoot(), model ) );
|
||||
// model->setFlags( 0 );
|
||||
mLayerTreeView->setModel( model );
|
||||
mLayerTreeView->resizeColumnToContents( 0 );
|
||||
mLayerTreeView->header()->show();
|
||||
mLayerTreeView->header()->setSectionResizeMode( QHeaderView::ResizeToContents );
|
||||
mLayerTreeView->header()->setSectionResizeMode( QHeaderView::Interactive );
|
||||
mLayerTreeView->setSelectionMode( QAbstractItemView::NoSelection );
|
||||
|
||||
// item delegates
|
||||
mLayerTreeView->setEditTriggers( QAbstractItemView::AllEditTriggers );
|
||||
mLayerTreeView->setItemDelegate( new QgsSnappingLayerDelegate( mCanvas, this ) );
|
||||
|
||||
QGridLayout* topLayout = new QGridLayout();
|
||||
topLayout->addLayout( layout, 0, 0, Qt::AlignLeft | Qt::AlignTop );
|
||||
topLayout->addWidget( mLayerTreeView, 1, 0 );
|
||||
setLayout( topLayout );
|
||||
}
|
||||
else
|
||||
{
|
||||
// mode = status bar
|
||||
setLayout( layout );
|
||||
}
|
||||
}
|
||||
|
||||
// connect settings changed and map units changed to properly update the widget
|
||||
connect( project, &QgsProject::snappingConfigChanged, this, &QgsSnappingWidget::projectSnapSettingsChanged );
|
||||
connect( mCanvas, SIGNAL( mapUnitsChanged() ), this, SLOT( updateToleranceDecimals() ) );
|
||||
|
||||
// modeChanged determines if widget are visible or not based on mode
|
||||
modeChanged();
|
||||
updateToleranceDecimals();
|
||||
}
|
||||
|
||||
QgsSnappingWidget::~QgsSnappingWidget()
|
||||
{
|
||||
if ( mDisplayMode == Widget )
|
||||
{
|
||||
QSettings().setValue( "/Windows/SnappingWidget/geometry", saveGeometry() );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::projectSnapSettingsChanged()
|
||||
{
|
||||
QgsSnappingConfig config = mProject->snappingConfig();
|
||||
if ( mConfig == config )
|
||||
return;
|
||||
mConfig = config;
|
||||
|
||||
mEnabledAction->setChecked( config.enabled() );
|
||||
|
||||
if ( config.mode() == QgsSnappingConfig::AllLayers && mModeButton->defaultAction() != mActiveLayerAction )
|
||||
{
|
||||
mModeButton->setDefaultAction( mAllLayersAction );
|
||||
modeChanged();
|
||||
updateToleranceDecimals();
|
||||
}
|
||||
if ( config.mode() == QgsSnappingConfig::ActiveLayer && mModeButton->defaultAction() != mActiveLayerAction )
|
||||
{
|
||||
mModeButton->setDefaultAction( mActiveLayerAction );
|
||||
modeChanged();
|
||||
updateToleranceDecimals();
|
||||
}
|
||||
if ( config.mode() == QgsSnappingConfig::AdvancedConfiguration && mModeButton->defaultAction() != mAdvancedModeAction )
|
||||
{
|
||||
mModeButton->setDefaultAction( mAdvancedModeAction );
|
||||
modeChanged();
|
||||
updateToleranceDecimals();
|
||||
}
|
||||
|
||||
if ( config.type() == QgsSnappingConfig::Vertex && mTypeButton->defaultAction() != mVertexAction )
|
||||
{
|
||||
mTypeButton->setDefaultAction( mVertexAction );
|
||||
}
|
||||
if ( config.type() == QgsSnappingConfig::VertexAndSegment && mTypeButton->defaultAction() != mVertexAndSegmentAction )
|
||||
{
|
||||
mTypeButton->setDefaultAction( mVertexAndSegmentAction );
|
||||
}
|
||||
if ( config.type() == QgsSnappingConfig::Segment && mTypeButton->defaultAction() != mSegmentAction )
|
||||
{
|
||||
mTypeButton->setDefaultAction( mSegmentAction );
|
||||
}
|
||||
|
||||
if ( mToleranceSpinBox->value() != config.tolerance() )
|
||||
{
|
||||
mToleranceSpinBox->setValue( config.tolerance() );
|
||||
}
|
||||
|
||||
if (( QgsTolerance::UnitType )mUnitsComboBox->currentData().toInt() != config.units() )
|
||||
{
|
||||
mUnitsComboBox->setCurrentIndex( mUnitsComboBox->findData( config.units() ) );
|
||||
}
|
||||
|
||||
if ( config.topologicalEditing() != mTopologicalEditingAction->isChecked() )
|
||||
{
|
||||
mTopologicalEditingAction->setChecked( config.topologicalEditing() );
|
||||
}
|
||||
|
||||
if ( config.intersectionSnapping() != mIntersectionSnappingAction->isChecked() )
|
||||
{
|
||||
mIntersectionSnappingAction->setChecked( config.intersectionSnapping() );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::enableSnapping( bool checked )
|
||||
{
|
||||
mModeButton->setEnabled( checked );
|
||||
mTypeButton->setEnabled( checked );
|
||||
mToleranceSpinBox->setEnabled( checked );
|
||||
mUnitsComboBox->setEnabled( checked );
|
||||
if ( mLayerTreeView )
|
||||
{
|
||||
mLayerTreeView->setEnabled( checked );
|
||||
}
|
||||
mTopologicalEditingAction->setEnabled( checked );
|
||||
mIntersectionSnappingAction->setEnabled( checked );
|
||||
|
||||
mConfig.setEnabled( checked );
|
||||
mProject->setSnappingConfig( mConfig );
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::changeTolerance( double tolerance )
|
||||
{
|
||||
mConfig.setTolerance( tolerance );
|
||||
mProject->setSnappingConfig( mConfig );
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::changeUnit( int idx )
|
||||
{
|
||||
QgsTolerance::UnitType unit = ( QgsTolerance::UnitType )mUnitsComboBox->itemData( idx ).toInt();
|
||||
mConfig.setUnits( unit );
|
||||
mProject->setSnappingConfig( mConfig );
|
||||
|
||||
updateToleranceDecimals();
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::enableTopologicalEditing( bool enabled )
|
||||
{
|
||||
mConfig.setTopologicalEditing( enabled );
|
||||
mProject->setSnappingConfig( mConfig );
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::enableIntersectionSnapping( bool enabled )
|
||||
{
|
||||
mConfig.setIntersectionSnapping( enabled );
|
||||
mProject->setSnappingConfig( mConfig );
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::modeButtonTriggered( QAction* action )
|
||||
{
|
||||
if ( action != mModeButton->defaultAction() )
|
||||
{
|
||||
mModeButton->setDefaultAction( action );
|
||||
if ( action == mAllLayersAction )
|
||||
{
|
||||
mConfig.setMode( QgsSnappingConfig::AllLayers );
|
||||
}
|
||||
else if ( action == mActiveLayerAction )
|
||||
{
|
||||
mConfig.setMode( QgsSnappingConfig::ActiveLayer );
|
||||
}
|
||||
else if ( action == mAdvancedModeAction )
|
||||
{
|
||||
mConfig.setMode( QgsSnappingConfig::AdvancedConfiguration );
|
||||
}
|
||||
mProject->setSnappingConfig( mConfig );
|
||||
updateToleranceDecimals();
|
||||
modeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::typeButtonTriggered( QAction* action )
|
||||
{
|
||||
if ( action != mTypeButton->defaultAction() )
|
||||
{
|
||||
mTypeButton->setDefaultAction( action );
|
||||
if ( action == mVertexAction )
|
||||
{
|
||||
mConfig.setType( QgsSnappingConfig::Vertex );
|
||||
}
|
||||
else if ( action == mVertexAndSegmentAction )
|
||||
{
|
||||
mConfig.setType( QgsSnappingConfig::VertexAndSegment );
|
||||
}
|
||||
else if ( action == mSegmentAction )
|
||||
{
|
||||
mConfig.setType( QgsSnappingConfig::Segment );
|
||||
}
|
||||
mProject->setSnappingConfig( mConfig );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::updateToleranceDecimals()
|
||||
{
|
||||
if ( mConfig.units() == QgsTolerance::Pixels )
|
||||
{
|
||||
mToleranceSpinBox->setDecimals( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsUnitTypes::DistanceUnit mapUnit = mCanvas->mapUnits();
|
||||
QgsUnitTypes::DistanceUnitType type = QgsUnitTypes::unitType( mapUnit );
|
||||
if ( type == QgsUnitTypes::Standard )
|
||||
{
|
||||
mToleranceSpinBox->setDecimals( 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
mToleranceSpinBox->setDecimals( 5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::modeChanged()
|
||||
{
|
||||
bool advanced = mConfig.mode() == QgsSnappingConfig::AdvancedConfiguration;
|
||||
|
||||
if ( mDisplayMode == ToolBar )
|
||||
{
|
||||
mTypeAction->setVisible( !advanced );
|
||||
mToleranceAction->setVisible( !advanced );
|
||||
mUnitAction->setVisible( !advanced );
|
||||
}
|
||||
else
|
||||
{
|
||||
mTypeButton->setVisible( !advanced );
|
||||
mToleranceSpinBox->setVisible( !advanced );
|
||||
mUnitsComboBox->setVisible( !advanced );
|
||||
if ( mDisplayMode == Widget && mLayerTreeView )
|
||||
{
|
||||
mLayerTreeView->setVisible( advanced );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QgsSnappingConfig QgsSnappingWidget::config() const
|
||||
{
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
void QgsSnappingWidget::setConfig( const QgsSnappingConfig& config )
|
||||
{
|
||||
if ( mConfig == config )
|
||||
return;
|
||||
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void QgsSnappingWidget::cleanGroup( QgsLayerTreeNode *node )
|
||||
{
|
||||
QgsLayerTreeGroup *group = QgsLayerTree::isGroup( node ) ? QgsLayerTree::toGroup( node ) : nullptr;
|
||||
if ( !group )
|
||||
return;
|
||||
|
||||
QList<QgsLayerTreeNode *> toRemove;
|
||||
Q_FOREACH ( QgsLayerTreeNode *child, node->children() )
|
||||
{
|
||||
if ( QgsLayerTree::isLayer( child ) && QgsLayerTree::toLayer( child )->layer()->type() != QgsMapLayer::VectorLayer )
|
||||
{
|
||||
toRemove << child;
|
||||
continue;
|
||||
}
|
||||
|
||||
cleanGroup( child );
|
||||
|
||||
if ( QgsLayerTree::isGroup( child ) && child->children().isEmpty() )
|
||||
toRemove << child;
|
||||
}
|
||||
|
||||
Q_FOREACH ( QgsLayerTreeNode *child, toRemove )
|
||||
group->removeChildNode( child );
|
||||
}
|
130
src/app/qgssnappingwidget.h
Normal file
@ -0,0 +1,130 @@
|
||||
/***************************************************************************
|
||||
qgssnappingwidget.h
|
||||
begin : August 2016
|
||||
copyright : (C) 2016 Denis Rouzaud
|
||||
email : denis.rouzaud@gmail.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSSNAPPINGWIDGET_H
|
||||
#define QGSSNAPPINGWIDGET_H
|
||||
|
||||
class QAction;
|
||||
class QComboBox;
|
||||
class QDoubleSpinBox;
|
||||
class QFont;
|
||||
class QToolButton;
|
||||
class QTreeView;
|
||||
|
||||
class QgsLayerTreeGroup;
|
||||
class QgsLayerTreeNode;
|
||||
class QgsLayerTreeView;
|
||||
class QgsMapCanvas;
|
||||
class QgsProject;
|
||||
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
/**
|
||||
* A widget which lets the user defines settings for snapping on a project
|
||||
* The widget can be displayed as a toolbar, in the status bar or as dialog/widget.
|
||||
* The display mode is automatically chose based on the parent widget type.
|
||||
*/
|
||||
class APP_EXPORT QgsSnappingWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param project The project with which this widget configuration will be synchronized
|
||||
* @param canvas the map canvas (used for map units)
|
||||
* @param parent is the parent widget. Based on the type of parent, it will
|
||||
* be displayed a tool bar, in the status bar or as a widget/dialog.
|
||||
*/
|
||||
QgsSnappingWidget( QgsProject* project, QgsMapCanvas* canvas, QWidget* parent = nullptr );
|
||||
|
||||
/** Destructor */
|
||||
virtual ~QgsSnappingWidget();
|
||||
|
||||
/**
|
||||
* The snapping configuration is what is managed by this widget.
|
||||
*/
|
||||
QgsSnappingConfig config() const;
|
||||
|
||||
/**
|
||||
* The snapping configuration is what is managed by this widget.
|
||||
*/
|
||||
void setConfig( const QgsSnappingConfig& config );
|
||||
|
||||
signals:
|
||||
void snappingConfigChanged( );
|
||||
|
||||
private slots:
|
||||
void projectSnapSettingsChanged();
|
||||
|
||||
void enableSnapping( bool checked );
|
||||
|
||||
void changeTolerance( double tolerance );
|
||||
|
||||
void changeUnit( int idx );
|
||||
|
||||
void enableTopologicalEditing( bool enabled );
|
||||
|
||||
void enableIntersectionSnapping( bool enabled );
|
||||
|
||||
void modeButtonTriggered( QAction* action );
|
||||
void typeButtonTriggered( QAction* action );
|
||||
|
||||
//! number of decimals of the tolerance spin box depends on map units
|
||||
void updateToleranceDecimals();
|
||||
|
||||
private:
|
||||
enum DisplayMode
|
||||
{
|
||||
ToolBar,
|
||||
StatusBar,
|
||||
Widget
|
||||
};
|
||||
DisplayMode mDisplayMode;
|
||||
|
||||
//! modeChanged determines if widget are visible or not based on mode
|
||||
void modeChanged();
|
||||
|
||||
QgsProject* mProject;
|
||||
QgsSnappingConfig mConfig;
|
||||
QgsMapCanvas* mCanvas;
|
||||
|
||||
QAction* mEnabledAction;
|
||||
QToolButton* mModeButton;
|
||||
QAction* mModeAction; // hide widget does not work on toolbar, action needed
|
||||
QAction* mAllLayersAction;
|
||||
QAction* mActiveLayerAction;
|
||||
QAction* mAdvancedModeAction;
|
||||
QToolButton* mTypeButton;
|
||||
QAction* mTypeAction; // hide widget does not work on toolbar, action needed
|
||||
QAction* mVertexAction;
|
||||
QAction* mSegmentAction;
|
||||
QAction* mVertexAndSegmentAction;
|
||||
QDoubleSpinBox* mToleranceSpinBox;
|
||||
QAction* mToleranceAction; // hide widget does not work on toolbar, action needed
|
||||
QComboBox* mUnitsComboBox;
|
||||
QAction* mUnitAction; // hide widget does not work on toolbar, action needed
|
||||
QAction* mTopologicalEditingAction;
|
||||
QAction* mIntersectionSnappingAction;
|
||||
QTreeView* mLayerTreeView;
|
||||
|
||||
void cleanGroup( QgsLayerTreeNode* node );
|
||||
};
|
||||
|
||||
#endif
|
@ -179,6 +179,7 @@ SET(QGIS_CORE_SRCS
|
||||
qgspointlocator.cpp
|
||||
qgsproject.cpp
|
||||
qgsprojectfiletransform.cpp
|
||||
qgssnappingconfig.cpp
|
||||
qgsprojectproperty.cpp
|
||||
qgsprojectversion.cpp
|
||||
qgsprovidermetadata.cpp
|
||||
@ -678,6 +679,7 @@ SET(QGIS_CORE_HDRS
|
||||
qgsproject.h
|
||||
qgsprojectfiletransform.h
|
||||
qgsprojectproperty.h
|
||||
qgssnappingconfig.h
|
||||
qgsprojectversion.h
|
||||
qgsprovidermetadata.h
|
||||
qgsproviderregistry.h
|
||||
@ -692,6 +694,7 @@ SET(QGIS_CORE_HDRS
|
||||
qgsscaleutils.h
|
||||
qgssimplifymethod.h
|
||||
qgssnapper.h
|
||||
qgssnappingconfig.h
|
||||
qgssnappingutils.h
|
||||
qgsspatialindex.h
|
||||
qgssqlexpressioncompiler.h
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "qgspluginlayer.h"
|
||||
#include "qgspluginlayerregistry.h"
|
||||
#include "qgsprojectfiletransform.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
#include "qgsprojectproperty.h"
|
||||
#include "qgsprojectversion.h"
|
||||
#include "qgsrasterlayer.h"
|
||||
@ -368,6 +369,7 @@ QgsProject::QgsProject()
|
||||
mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this );
|
||||
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersAdded( QList<QgsMapLayer*> ) ), this, SLOT( onMapLayersAdded( QList<QgsMapLayer*> ) ) );
|
||||
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersRemoved( QStringList ) ), this, SLOT( cleanTransactionGroups() ) );
|
||||
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QList<QgsMapLayer*> ) ), this, SLOT( onMapLayersRemoved( QList<QgsMapLayer*> ) ) );
|
||||
}
|
||||
|
||||
|
||||
@ -479,6 +481,7 @@ void QgsProject::clear()
|
||||
imp_->clear();
|
||||
mEmbeddedLayers.clear();
|
||||
mRelationManager->clear();
|
||||
mSnappingConfig.reset();
|
||||
|
||||
mMapThemeCollection.reset( new QgsMapThemeCollection() );
|
||||
|
||||
@ -632,6 +635,21 @@ void QgsProject::processLayerJoins( QgsVectorLayer* layer )
|
||||
layer->updateFields();
|
||||
}
|
||||
|
||||
QgsSnappingConfig QgsProject::snappingConfig() const
|
||||
{
|
||||
return mSnappingConfig;
|
||||
}
|
||||
|
||||
void QgsProject::setSnappingConfig( const QgsSnappingConfig& snappingConfig )
|
||||
{
|
||||
if ( mSnappingConfig == snappingConfig )
|
||||
return;
|
||||
|
||||
mSnappingConfig = snappingConfig;
|
||||
setDirty();
|
||||
emit snappingConfigChanged();
|
||||
}
|
||||
|
||||
bool QgsProject::_getMapLayers( const QDomDocument& doc, QList<QDomNode>& brokenNodes )
|
||||
{
|
||||
// Layer order is set by the restoring the legend settings from project file.
|
||||
@ -926,6 +944,9 @@ bool QgsProject::read()
|
||||
it.value()->setDependencies( it.value()->dependencies() );
|
||||
}
|
||||
|
||||
mSnappingConfig.readProject( *doc );
|
||||
emit snappingConfigChanged();
|
||||
|
||||
// read the project: used by map canvas and legend
|
||||
emit readProject( *doc );
|
||||
|
||||
@ -1039,6 +1060,15 @@ void QgsProject::onMapLayersAdded( const QList<QgsMapLayer*>& layers )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( mSnappingConfig.addLayers( layers ) )
|
||||
emit snappingConfigChanged();
|
||||
}
|
||||
|
||||
void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer*>& layers )
|
||||
{
|
||||
if ( mSnappingConfig.removeLayers( layers ) )
|
||||
emit snappingConfigChanged();
|
||||
}
|
||||
|
||||
void QgsProject::cleanTransactionGroups( bool force )
|
||||
@ -1143,6 +1173,8 @@ bool QgsProject::write()
|
||||
clonedRoot->writeXml( qgisNode );
|
||||
delete clonedRoot;
|
||||
|
||||
mSnappingConfig.writeProject( *doc );
|
||||
|
||||
// let map canvas and legend write their information
|
||||
emit writeProject( *doc );
|
||||
|
||||
@ -1975,134 +2007,6 @@ void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsL
|
||||
}
|
||||
}
|
||||
|
||||
void QgsProject::setSnapSettingsForLayer( const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
|
||||
{
|
||||
QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
|
||||
snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
|
||||
int idx = layerIdList.indexOf( layerId );
|
||||
if ( idx != -1 )
|
||||
{
|
||||
layerIdList.removeAt( idx );
|
||||
enabledList.removeAt( idx );
|
||||
snapTypeList.removeAt( idx );
|
||||
toleranceUnitList.removeAt( idx );
|
||||
toleranceList.removeAt( idx );
|
||||
avoidIntersectionList.removeOne( layerId );
|
||||
}
|
||||
|
||||
layerIdList.append( layerId );
|
||||
|
||||
// enabled
|
||||
enabledList.append( enabled ? "enabled" : "disabled" );
|
||||
|
||||
// snap type
|
||||
QString typeString;
|
||||
if ( type == QgsSnapper::SnapToSegment )
|
||||
{
|
||||
typeString = "to_segment";
|
||||
}
|
||||
else if ( type == QgsSnapper::SnapToVertexAndSegment )
|
||||
{
|
||||
typeString = "to_vertex_and_segment";
|
||||
}
|
||||
else
|
||||
{
|
||||
typeString = "to_vertex";
|
||||
}
|
||||
snapTypeList.append( typeString );
|
||||
|
||||
// units
|
||||
toleranceUnitList.append( QString::number( unit ) );
|
||||
|
||||
// tolerance
|
||||
toleranceList.append( QString::number( tolerance ) );
|
||||
|
||||
// avoid intersection
|
||||
if ( avoidIntersection )
|
||||
{
|
||||
avoidIntersectionList.append( layerId );
|
||||
}
|
||||
|
||||
writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
|
||||
writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
|
||||
writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
|
||||
writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
|
||||
writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
|
||||
writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
|
||||
emit snapSettingsChanged();
|
||||
}
|
||||
|
||||
bool QgsProject::snapSettingsForLayer( const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance,
|
||||
bool &avoidIntersection ) const
|
||||
{
|
||||
QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
|
||||
snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
|
||||
int idx = layerIdList.indexOf( layerId );
|
||||
if ( idx == -1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure all lists are long enough
|
||||
int minListEntries = idx + 1;
|
||||
if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
|
||||
toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// enabled
|
||||
enabled = enabledList.at( idx ) == "enabled";
|
||||
|
||||
// snap type
|
||||
QString snapType = snapTypeList.at( idx );
|
||||
if ( snapType == "to_segment" )
|
||||
{
|
||||
type = QgsSnapper::SnapToSegment;
|
||||
}
|
||||
else if ( snapType == "to_vertex_and_segment" )
|
||||
{
|
||||
type = QgsSnapper::SnapToVertexAndSegment;
|
||||
}
|
||||
else // to vertex
|
||||
{
|
||||
type = QgsSnapper::SnapToVertex;
|
||||
}
|
||||
|
||||
// units
|
||||
if ( toleranceUnitList.at( idx ) == "1" )
|
||||
{
|
||||
units = QgsTolerance::Pixels;
|
||||
}
|
||||
else if ( toleranceUnitList.at( idx ) == "2" )
|
||||
{
|
||||
units = QgsTolerance::ProjectUnits;
|
||||
}
|
||||
else
|
||||
{
|
||||
units = QgsTolerance::LayerUnits;
|
||||
}
|
||||
|
||||
// tolerance
|
||||
tolerance = toleranceList.at( idx ).toDouble();
|
||||
|
||||
// avoid intersection
|
||||
avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsProject::snapSettings( QStringList &layerIdList, QStringList &enabledList, QStringList &snapTypeList, QStringList &toleranceUnitList, QStringList &toleranceList,
|
||||
QStringList &avoidIntersectionList ) const
|
||||
{
|
||||
layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
|
||||
enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
|
||||
toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
|
||||
toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
|
||||
snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
|
||||
avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
|
||||
}
|
||||
|
||||
bool QgsProject::evaluateDefaultValues() const
|
||||
{
|
||||
return imp_->evaluateDefaultValues;
|
||||
@ -2125,7 +2029,7 @@ void QgsProject::setEvaluateDefaultValues( bool evaluateDefaultValues )
|
||||
void QgsProject::setTopologicalEditing( bool enabled )
|
||||
{
|
||||
QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
|
||||
emit snapSettingsChanged();
|
||||
// todo emit snapSettingsChanged();
|
||||
}
|
||||
|
||||
bool QgsProject::topologicalEditing() const
|
||||
|
@ -32,6 +32,7 @@
|
||||
//#include <QDomDocument>
|
||||
#include "qgssnapper.h"
|
||||
#include "qgsunittypes.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
#include "qgsprojectversion.h"
|
||||
#include "qgsexpressioncontextgenerator.h"
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
@ -44,12 +45,13 @@ class QDomNode;
|
||||
class QgsLayerTreeGroup;
|
||||
class QgsLayerTreeRegistryBridge;
|
||||
class QgsMapLayer;
|
||||
class QgsMapThemeCollection;
|
||||
class QgsProjectBadLayerHandler;
|
||||
class QgsRelationManager;
|
||||
class QgsVectorLayer;
|
||||
class QgsMapThemeCollection;
|
||||
class QgsTransactionGroup;
|
||||
class QgsTolerance;
|
||||
class QgsTransactionGroup;
|
||||
class QgsVectorLayer;
|
||||
|
||||
|
||||
/** \ingroup core
|
||||
* Reads and writes project states.
|
||||
@ -77,6 +79,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
Q_PROPERTY( QString homePath READ homePath NOTIFY homePathChanged )
|
||||
Q_PROPERTY( QgsCoordinateReferenceSystem crs READ crs WRITE setCrs )
|
||||
Q_PROPERTY( QgsMapThemeCollection* mapThemeCollection READ mapThemeCollection )
|
||||
Q_PROPERTY( QgsSnappingConfig snappingConfig READ snappingConfig WRITE setSnappingConfig NOTIFY snappingConfigChanged )
|
||||
|
||||
public:
|
||||
|
||||
@ -333,14 +336,6 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
*/
|
||||
QgsLayerTreeGroup* createEmbeddedGroup( const QString& groupName, const QString& projectFilePath, const QStringList &invisibleLayers );
|
||||
|
||||
/** Convenience function to set snap settings per layer */
|
||||
void setSnapSettingsForLayer( const QString& layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance,
|
||||
bool avoidIntersection );
|
||||
|
||||
/** Convenience function to query snap settings of a layer */
|
||||
bool snapSettingsForLayer( const QString& layerId, bool& enabled, QgsSnapper::SnappingType& type, QgsTolerance::UnitType& units, double& tolerance,
|
||||
bool& avoidIntersection ) const;
|
||||
|
||||
/** Convenience function to set topological editing */
|
||||
void setTopologicalEditing( bool enabled );
|
||||
|
||||
@ -460,33 +455,26 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
|
||||
QgsExpressionContext createExpressionContext() const override;
|
||||
|
||||
protected:
|
||||
/** Set error message from read/write operation
|
||||
* @note not available in Python bindings
|
||||
/**
|
||||
* The snapping configuration for this project.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
void setError( const QString& errorMessage );
|
||||
QgsSnappingConfig snappingConfig() const;
|
||||
|
||||
/** Clear error message
|
||||
* @note not available in Python bindings
|
||||
/**
|
||||
* The snapping configuration for this project.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
void clearError();
|
||||
|
||||
//! Creates layer and adds it to maplayer registry
|
||||
//! @note not available in python bindings
|
||||
bool addLayer( const QDomElement& layerElem, QList<QDomNode>& brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList );
|
||||
|
||||
//! @note not available in python bindings
|
||||
void initializeEmbeddedSubtree( const QString& projectFilePath, QgsLayerTreeGroup* group );
|
||||
|
||||
//! @note not available in python bindings
|
||||
void loadEmbeddedNodes( QgsLayerTreeGroup* group );
|
||||
void setSnappingConfig( const QgsSnappingConfig& snappingConfig );
|
||||
|
||||
signals:
|
||||
//! emitted when project is being read
|
||||
void readProject( const QDomDocument & );
|
||||
void readProject( const QDomDocument& );
|
||||
|
||||
//! emitted when project is being written
|
||||
void writeProject( QDomDocument & );
|
||||
void writeProject( QDomDocument& );
|
||||
|
||||
/**
|
||||
* Emitted, after the basic initialization of a layer from the project
|
||||
@ -496,7 +484,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
* @param mapLayer The map layer which is being initialized
|
||||
* @param layerNode The layer node from the project file
|
||||
*/
|
||||
void readMapLayer( QgsMapLayer *mapLayer, const QDomElement &layerNode );
|
||||
void readMapLayer( QgsMapLayer* mapLayer, const QDomElement& layerNode );
|
||||
|
||||
/**
|
||||
* Emitted, when a layer is being saved. You can use this method to save
|
||||
@ -523,8 +511,6 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
|
||||
void loadingLayer( const QString& );
|
||||
|
||||
void snapSettingsChanged();
|
||||
|
||||
//! Emitted when the list of layer which are excluded from map identification changes
|
||||
void nonIdentifiableLayersChanged( QStringList nonIdentifiableLayers );
|
||||
|
||||
@ -534,6 +520,9 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
//! Emitted when the home path of the project changes
|
||||
void homePathChanged();
|
||||
|
||||
//! emitted whenever the configuration for snapping has changed
|
||||
void snappingConfigChanged();
|
||||
|
||||
/** Emitted whenever the expression variables stored in the project have been changed.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
@ -566,6 +555,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
|
||||
private slots:
|
||||
void onMapLayersAdded( const QList<QgsMapLayer*>& layers );
|
||||
void onMapLayersRemoved( const QList<QgsMapLayer*>& layers );
|
||||
void cleanTransactionGroups( bool force = false );
|
||||
|
||||
private:
|
||||
@ -594,6 +584,26 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
*/
|
||||
void processLayerJoins( QgsVectorLayer* layer );
|
||||
|
||||
/** Set error message from read/write operation
|
||||
* @note not available in Python bindings
|
||||
*/
|
||||
void setError( const QString& errorMessage );
|
||||
|
||||
/** Clear error message
|
||||
* @note not available in Python bindings
|
||||
*/
|
||||
void clearError();
|
||||
|
||||
//! Creates layer and adds it to maplayer registry
|
||||
//! @note not available in python bindings
|
||||
bool addLayer( const QDomElement& layerElem, QList<QDomNode>& brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > >& vectorLayerList );
|
||||
|
||||
//! @note not available in python bindings
|
||||
void initializeEmbeddedSubtree( const QString& projectFilePath, QgsLayerTreeGroup* group );
|
||||
|
||||
//! @note not available in python bindings
|
||||
void loadEmbeddedNodes( QgsLayerTreeGroup* group );
|
||||
|
||||
QString mErrorMessage;
|
||||
|
||||
QgsProjectBadLayerHandler* mBadLayerHandler;
|
||||
@ -604,8 +614,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
|
||||
*/
|
||||
QHash< QString, QPair< QString, bool> > mEmbeddedLayers;
|
||||
|
||||
void snapSettings( QStringList& layerIdList, QStringList& enabledList, QStringList& snapTypeList, QStringList& snapUnitList, QStringList& toleranceUnitList,
|
||||
QStringList& avoidIntersectionList ) const;
|
||||
QgsSnappingConfig mSnappingConfig;
|
||||
|
||||
QgsRelationManager* mRelationManager;
|
||||
|
||||
|
509
src/core/qgssnappingconfig.cpp
Normal file
@ -0,0 +1,509 @@
|
||||
/***************************************************************************
|
||||
qgsprojectsnappingsettings.cpp - QgsProjectSnappingSettings
|
||||
|
||||
---------------------
|
||||
begin : 29.8.2016
|
||||
copyright : (C) 2016 by Denis Rouzaud
|
||||
email : denis.rouzaud@gmail.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QHeaderView>
|
||||
#include <QSettings>
|
||||
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmaplayerregistry.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsproject.h"
|
||||
|
||||
QgsSnappingConfig::IndividualLayerSettings::IndividualLayerSettings()
|
||||
: mValid( false )
|
||||
, mEnabled( false )
|
||||
, mType( Vertex )
|
||||
, mTolerance( 0 )
|
||||
, mUnits( QgsTolerance::Pixels )
|
||||
, mAvoidIntersection( false )
|
||||
{}
|
||||
|
||||
|
||||
QgsSnappingConfig::IndividualLayerSettings::IndividualLayerSettings( bool enabled, SnappingType type, double tolerance, QgsTolerance::UnitType units, bool avoidIntersection )
|
||||
: mValid( true )
|
||||
, mEnabled( enabled )
|
||||
, mType( type )
|
||||
, mTolerance( tolerance )
|
||||
, mUnits( units )
|
||||
, mAvoidIntersection( avoidIntersection )
|
||||
{}
|
||||
|
||||
bool QgsSnappingConfig::IndividualLayerSettings::valid() const
|
||||
{
|
||||
return mValid;
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::IndividualLayerSettings::enabled() const
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::IndividualLayerSettings::setEnabled( bool enabled )
|
||||
{
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
QgsSnappingConfig::SnappingType QgsSnappingConfig::IndividualLayerSettings::type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::IndividualLayerSettings::setType( SnappingType type )
|
||||
{
|
||||
mType = type;
|
||||
}
|
||||
|
||||
double QgsSnappingConfig::IndividualLayerSettings::tolerance() const
|
||||
{
|
||||
return mTolerance;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::IndividualLayerSettings::setTolerance( double tolerance )
|
||||
{
|
||||
mTolerance = tolerance;
|
||||
}
|
||||
|
||||
QgsTolerance::UnitType QgsSnappingConfig::IndividualLayerSettings::units() const
|
||||
{
|
||||
return mUnits;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::IndividualLayerSettings::setUnits( QgsTolerance::UnitType units )
|
||||
{
|
||||
mUnits = units;
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::IndividualLayerSettings::avoidIntersection() const
|
||||
{
|
||||
return mAvoidIntersection;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::IndividualLayerSettings::setAvoidIntersection( bool avoidIntersection )
|
||||
{
|
||||
mAvoidIntersection = avoidIntersection;
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::IndividualLayerSettings::operator !=( const QgsSnappingConfig::IndividualLayerSettings& other ) const
|
||||
{
|
||||
return mValid != other.mValid
|
||||
|| mEnabled != other.mEnabled
|
||||
|| mType != other.mType
|
||||
|| mTolerance != other.mTolerance
|
||||
|| mUnits != other.mUnits
|
||||
|| mAvoidIntersection != other.mAvoidIntersection;
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::IndividualLayerSettings::operator ==( const QgsSnappingConfig::IndividualLayerSettings& other ) const
|
||||
{
|
||||
return mValid == other.mValid
|
||||
&& mEnabled == other.mEnabled
|
||||
&& mType == other.mType
|
||||
&& mTolerance == other.mTolerance
|
||||
&& mUnits == other.mUnits
|
||||
&& mAvoidIntersection == other.mAvoidIntersection;
|
||||
}
|
||||
|
||||
|
||||
QgsSnappingConfig::QgsSnappingConfig()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
QgsSnappingConfig::~QgsSnappingConfig()
|
||||
{
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::operator==( const QgsSnappingConfig& other ) const
|
||||
{
|
||||
return mEnabled == other.mEnabled
|
||||
&& mMode == other.mMode
|
||||
&& mType == other.mType
|
||||
&& mTolerance == other.mTolerance
|
||||
&& mUnits == other.mUnits
|
||||
&& mTopologicalEditing == other.mTopologicalEditing
|
||||
&& mIntersectionSnapping == other.mIntersectionSnapping
|
||||
&& mIndividualLayerSettings == other.mIndividualLayerSettings;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::reset()
|
||||
{
|
||||
// get defaults values. They are both used for standard and advanced configuration (per layer)
|
||||
bool enabled = QSettings().value( "/qgis/digitizing/default_advanced_snap_enabled", true ).toBool();
|
||||
SnappingMode mode = ( SnappingMode )QSettings().value( "/qgis/digitizing/default_snap_mode", ( int )AllLayers ).toInt();
|
||||
if ( mMode == 0 )
|
||||
{
|
||||
// backward compatibility with QGIS 2.x
|
||||
// could be removed in 3.4+
|
||||
mMode = AllLayers;
|
||||
}
|
||||
SnappingType type = ( SnappingType )QSettings().value( "/qgis/digitizing/default_snap_type", ( int )Vertex ).toInt();
|
||||
double tolerance = QSettings().value( "/qgis/digitizing/default_snapping_tolerance", 0 ).toDouble();
|
||||
QgsTolerance::UnitType units = ( QgsTolerance::UnitType )QSettings().value( "/qgis/digitizing/default_snapping_tolerance_unit", ( int )QgsTolerance::ProjectUnits ).toInt();
|
||||
|
||||
// assign main (standard) config
|
||||
mEnabled = enabled;
|
||||
mMode = mode;
|
||||
mType = type;
|
||||
mTolerance = tolerance;
|
||||
// do not allow unit to be "layer" if not in advanced configuration
|
||||
if ( mMode != AdvancedConfiguration )
|
||||
{
|
||||
mUnits = QgsTolerance::ProjectUnits;
|
||||
}
|
||||
else
|
||||
{
|
||||
mUnits = units;
|
||||
}
|
||||
mTopologicalEditing = false;
|
||||
mIntersectionSnapping = false;
|
||||
|
||||
// set advanced config
|
||||
mIndividualLayerSettings = QHash<QgsVectorLayer *, IndividualLayerSettings>();
|
||||
Q_FOREACH ( QgsMapLayer *ml, QgsMapLayerRegistry::instance()->mapLayers() )
|
||||
{
|
||||
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( ml );
|
||||
if ( vl )
|
||||
{
|
||||
mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::enabled() const
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setEnabled( bool enabled )
|
||||
{
|
||||
if ( mEnabled == enabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
QgsSnappingConfig::SnappingMode QgsSnappingConfig::mode() const
|
||||
{
|
||||
return mMode;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setMode( QgsSnappingConfig::SnappingMode mode )
|
||||
{
|
||||
if ( mMode == mode )
|
||||
{
|
||||
return;
|
||||
}
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
QgsSnappingConfig::SnappingType QgsSnappingConfig::type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setType( QgsSnappingConfig::SnappingType type )
|
||||
{
|
||||
if ( mType == type )
|
||||
{
|
||||
return;
|
||||
}
|
||||
mType = type;
|
||||
}
|
||||
|
||||
double QgsSnappingConfig::tolerance() const
|
||||
{
|
||||
return mTolerance;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setTolerance( double tolerance )
|
||||
{
|
||||
if ( mTolerance == tolerance )
|
||||
{
|
||||
return;
|
||||
}
|
||||
mTolerance = tolerance;
|
||||
}
|
||||
|
||||
QgsTolerance::UnitType QgsSnappingConfig::units() const
|
||||
{
|
||||
return mUnits;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setUnits( QgsTolerance::UnitType units )
|
||||
{
|
||||
if ( mUnits == units )
|
||||
{
|
||||
return;
|
||||
}
|
||||
mUnits = units;
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::topologicalEditing() const
|
||||
{
|
||||
return mTopologicalEditing;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setTopologicalEditing( bool enabled )
|
||||
{
|
||||
mTopologicalEditing = enabled;
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::intersectionSnapping() const
|
||||
{
|
||||
return mIntersectionSnapping;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setIntersectionSnapping( bool enabled )
|
||||
{
|
||||
mIntersectionSnapping = enabled;
|
||||
}
|
||||
|
||||
QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> QgsSnappingConfig::individualLayerSettings() const
|
||||
{
|
||||
return mIndividualLayerSettings;
|
||||
}
|
||||
|
||||
QgsSnappingConfig::IndividualLayerSettings QgsSnappingConfig::individualLayerSettings( QgsVectorLayer* vl ) const
|
||||
{
|
||||
if ( vl && mIndividualLayerSettings.contains( vl ) )
|
||||
{
|
||||
return mIndividualLayerSettings.value( vl );
|
||||
}
|
||||
else
|
||||
{
|
||||
// return invalid settings
|
||||
return IndividualLayerSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::setIndividualLayerSettings( QgsVectorLayer* vl, IndividualLayerSettings individualLayerSettings )
|
||||
{
|
||||
if ( !vl || mIndividualLayerSettings.value( vl ) == individualLayerSettings )
|
||||
{
|
||||
return;
|
||||
}
|
||||
mIndividualLayerSettings.insert( vl, individualLayerSettings );
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::operator!=( const QgsSnappingConfig& other ) const
|
||||
{
|
||||
return mEnabled != other.mEnabled
|
||||
|| mMode != other.mMode
|
||||
|| mType != other.mType
|
||||
|| mTolerance != other.mTolerance
|
||||
|| mUnits != other.mUnits
|
||||
|| mIndividualLayerSettings != other.mIndividualLayerSettings;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::readProject( const QDomDocument& doc )
|
||||
{
|
||||
QDomElement snapSettingsElem = doc.firstChildElement( "qgis" ).firstChildElement( "snapping-settings" );
|
||||
if ( snapSettingsElem.isNull() )
|
||||
{
|
||||
readLegacySettings();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( snapSettingsElem.hasAttribute( "enabled" ) )
|
||||
mEnabled = snapSettingsElem.attribute( "enabled" ) == "1";
|
||||
|
||||
if ( snapSettingsElem.hasAttribute( "mode" ) )
|
||||
mMode = ( SnappingMode )snapSettingsElem.attribute( "mode" ).toInt();
|
||||
|
||||
if ( snapSettingsElem.hasAttribute( "type" ) )
|
||||
mType = ( SnappingType )snapSettingsElem.attribute( "type" ).toInt();
|
||||
|
||||
if ( snapSettingsElem.hasAttribute( "tolerance" ) )
|
||||
mTolerance = snapSettingsElem.attribute( "tolerance" ).toDouble();
|
||||
|
||||
if ( snapSettingsElem.hasAttribute( "unit" ) )
|
||||
mUnits = ( QgsTolerance::UnitType )snapSettingsElem.attribute( "unit" ).toInt();
|
||||
|
||||
if ( snapSettingsElem.hasAttribute( "topological-editing" ) )
|
||||
mTopologicalEditing = snapSettingsElem.attribute( "topological-editing" ) == "1";
|
||||
|
||||
if ( snapSettingsElem.hasAttribute( "intersection-snapping" ) )
|
||||
mIntersectionSnapping = snapSettingsElem.attribute( "intersection-snapping" ) == "1";
|
||||
|
||||
// do not clear the settings as they must be automatically synchronized with current layers
|
||||
QDomNodeList nodes = snapSettingsElem.elementsByTagName( "individual-layer-settings" );
|
||||
if ( nodes.count() )
|
||||
{
|
||||
QDomNode node = nodes.item( 0 );
|
||||
QDomNodeList settingNodes = node.childNodes();
|
||||
int layerCount = settingNodes.count();
|
||||
for ( int i = 0; i < layerCount; ++i )
|
||||
{
|
||||
QDomElement settingElement = settingNodes.at( i ).toElement();
|
||||
if ( settingElement.tagName() != "layer-setting" )
|
||||
{
|
||||
QgsLogger::warning( QApplication::translate( "QgsProjectSnappingSettings", "Cannot read individual settings. Unexpected tag '%1'" ).arg( settingElement.tagName() ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
QString layerId = settingElement.attribute( "id" );
|
||||
bool enabled = settingElement.attribute( "enabled" ) == "1";
|
||||
SnappingType type = ( SnappingType )settingElement.attribute( "type" ).toInt();
|
||||
double tolerance = settingElement.attribute( "tolerance" ).toDouble();
|
||||
QgsTolerance::UnitType units = ( QgsTolerance::UnitType )settingElement.attribute( "units" ).toInt();
|
||||
bool avoidIntersection = settingElement.attribute( "avoid-intersection" ) == "1";
|
||||
|
||||
QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
|
||||
if ( !ml || ml->type() != QgsMapLayer::VectorLayer )
|
||||
continue;
|
||||
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml );
|
||||
|
||||
IndividualLayerSettings setting = IndividualLayerSettings( enabled, type, tolerance, units, avoidIntersection );
|
||||
mIndividualLayerSettings.insert( vl, setting );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::writeProject( QDomDocument& doc )
|
||||
{
|
||||
QDomElement snapSettingsElem = doc.createElement( "snapping-settings" );
|
||||
snapSettingsElem.setAttribute( "enabled", QString::number( mEnabled ) );
|
||||
snapSettingsElem.setAttribute( "mode", ( int )mMode );
|
||||
snapSettingsElem.setAttribute( "type", ( int )mType );
|
||||
snapSettingsElem.setAttribute( "tolerance", mTolerance );
|
||||
snapSettingsElem.setAttribute( "unit", ( int )mUnits );
|
||||
snapSettingsElem.setAttribute( "topological-editing", QString::number( mTopologicalEditing ) );
|
||||
snapSettingsElem.setAttribute( "intersection-snapping", QString::number( mIntersectionSnapping ) );
|
||||
|
||||
QDomElement ilsElement = doc.createElement( "individual-layer-settings" );
|
||||
Q_FOREACH ( QgsVectorLayer* vl, mIndividualLayerSettings.keys() )
|
||||
{
|
||||
IndividualLayerSettings setting = mIndividualLayerSettings.value( vl );
|
||||
|
||||
QDomElement layerElement = doc.createElement( "layer-setting" );
|
||||
layerElement.setAttribute( "id", vl->id() );
|
||||
layerElement.setAttribute( "enabled", QString::number( setting.enabled() ) );
|
||||
layerElement.setAttribute( "type", ( int )setting.type() );
|
||||
layerElement.setAttribute( "tolerance", setting.tolerance() );
|
||||
layerElement.setAttribute( "units", ( int )setting.units() );
|
||||
layerElement.setAttribute( "avoid-intersection", QString::number( setting.avoidIntersection() ) );
|
||||
ilsElement.appendChild( layerElement );
|
||||
}
|
||||
snapSettingsElem.appendChild( ilsElement );
|
||||
|
||||
doc.firstChildElement( "qgis" ).appendChild( snapSettingsElem );
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::addLayers( const QList<QgsMapLayer*>& layers )
|
||||
{
|
||||
bool changed = false;
|
||||
bool enabled = QSettings().value( "/qgis/digitizing/default_advanced_snap_enabled", true ).toBool();
|
||||
SnappingType type = ( SnappingType )QSettings().value( "/qgis/digitizing/default_snap_type", Vertex ).toInt();
|
||||
double tolerance = QSettings().value( "/qgis/digitizing/default_snapping_tolerance", 0 ).toDouble();
|
||||
QgsTolerance::UnitType units = ( QgsTolerance::UnitType )QSettings().value( "/qgis/digitizing/default_snapping_tolerance_unit", QgsTolerance::ProjectUnits ).toInt();
|
||||
|
||||
Q_FOREACH ( QgsMapLayer* ml, layers )
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml );
|
||||
if ( vl )
|
||||
{
|
||||
mIndividualLayerSettings.insert( vl, IndividualLayerSettings( enabled, type, tolerance, units ) );
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool QgsSnappingConfig::removeLayers( const QList<QgsMapLayer*>& layers )
|
||||
{
|
||||
bool changed = false;
|
||||
Q_FOREACH ( QgsMapLayer* ml, layers )
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( ml );
|
||||
if ( vl )
|
||||
{
|
||||
mIndividualLayerSettings.remove( vl );
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void QgsSnappingConfig::readLegacySettings()
|
||||
{
|
||||
mMode = ActiveLayer;
|
||||
mIndividualLayerSettings.clear();
|
||||
|
||||
QString snapMode = QgsProject::instance()->readEntry( "Digitizing", "/SnappingMode" );
|
||||
|
||||
QString snapType = QgsProject::instance()->readEntry( "Digitizing", "/DefaultSnapType", QString( "off" ) );
|
||||
if ( snapType == "to segment" )
|
||||
mType = Segment;
|
||||
else if ( snapType == "to vertex and segment" )
|
||||
mType = VertexAndSegment;
|
||||
else if ( snapType == "to vertex" )
|
||||
mType = Vertex;
|
||||
mTolerance = QgsProject::instance()->readDoubleEntry( "Digitizing", "/DefaultSnapTolerance", 0 );
|
||||
mUnits = static_cast< QgsTolerance::UnitType >( QgsProject::instance()->readNumEntry( "Digitizing", "/DefaultSnapToleranceUnit", QgsTolerance::ProjectUnits ) );
|
||||
|
||||
mIntersectionSnapping = QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 );
|
||||
|
||||
//read snapping settings from project
|
||||
bool snappingDefinedInProject, ok;
|
||||
QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &snappingDefinedInProject );
|
||||
QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok );
|
||||
QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok );
|
||||
QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok );
|
||||
QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok );
|
||||
|
||||
// lists must have the same size, otherwise something is wrong
|
||||
if ( layerIdList.size() != enabledList.size() ||
|
||||
layerIdList.size() != toleranceList.size() ||
|
||||
layerIdList.size() != toleranceUnitList.size() ||
|
||||
layerIdList.size() != snapToList.size() )
|
||||
return;
|
||||
|
||||
if ( !snappingDefinedInProject )
|
||||
return; // nothing defined in project - use current layer
|
||||
|
||||
// Use snapping information from the project
|
||||
if ( snapMode == "current_layer" )
|
||||
mMode = ActiveLayer;
|
||||
else if ( snapMode == "all_layers" )
|
||||
mMode = AllLayers;
|
||||
else // either "advanced" or empty (for background compatibility)
|
||||
mMode = AdvancedConfiguration;
|
||||
|
||||
// load layers, tolerances, snap type
|
||||
QStringList::const_iterator layerIt( layerIdList.constBegin() );
|
||||
QStringList::const_iterator tolIt( toleranceList.constBegin() );
|
||||
QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
|
||||
QStringList::const_iterator snapIt( snapToList.constBegin() );
|
||||
QStringList::const_iterator enabledIt( enabledList.constBegin() );
|
||||
for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
|
||||
{
|
||||
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) );
|
||||
if ( !vlayer || !vlayer->hasGeometryType() )
|
||||
continue;
|
||||
|
||||
SnappingType t( *snapIt == "to_vertex" ? Vertex :
|
||||
( *snapIt == "to_segment" ? Segment :
|
||||
VertexAndSegment
|
||||
)
|
||||
);
|
||||
|
||||
mIndividualLayerSettings.insert( vlayer, IndividualLayerSettings( *enabledIt == "enabled", t, tolIt->toDouble(), static_cast<QgsTolerance::UnitType>( tolUnitIt->toInt() ) ) );
|
||||
}
|
||||
}
|
245
src/core/qgssnappingconfig.h
Normal file
@ -0,0 +1,245 @@
|
||||
/***************************************************************************
|
||||
qgssnappingconfig.h - QgsSnappingConfig
|
||||
|
||||
---------------------
|
||||
begin : 29.8.2016
|
||||
copyright : (C) 2016 by Denis Rouzaud
|
||||
email : denis.rouzaud@gmail.com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSPROJECTSNAPPINGSETTINGS_H
|
||||
#define QGSPROJECTSNAPPINGSETTINGS_H
|
||||
|
||||
#include "qgstolerance.h"
|
||||
|
||||
class QDomDocument;
|
||||
class QgsVectorLayer;
|
||||
|
||||
|
||||
/** \ingroup core
|
||||
* This is a container for configuration of the snapping of the project
|
||||
* @note added in 3.0
|
||||
*/
|
||||
class CORE_EXPORT QgsSnappingConfig
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* SnappingMode defines on which layer the snapping is performed
|
||||
*/
|
||||
enum SnappingMode
|
||||
{
|
||||
ActiveLayer = 1, /*!< on the active layer */
|
||||
AllLayers = 2, /*!< on all vector layers */
|
||||
AdvancedConfiguration = 3, /*!< on a per layer configuration basis */
|
||||
};
|
||||
|
||||
/**
|
||||
* SnappingType defines on what object the snapping is performed
|
||||
*/
|
||||
enum SnappingType
|
||||
{
|
||||
Vertex = 1, /*!< on vertices only */
|
||||
VertexAndSegment = 2, /*!< both on vertices and segments */
|
||||
Segment = 3, /*!< on segments only */
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* This is a container of advanced configuration (per layer) of the snapping of the project
|
||||
* @note added in 3.0
|
||||
*/
|
||||
class CORE_EXPORT IndividualLayerSettings
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief IndividualLayerSettings
|
||||
* @param enabled
|
||||
* @param type
|
||||
* @param tolerance
|
||||
* @param units
|
||||
* @param avoidIntersection
|
||||
*/
|
||||
IndividualLayerSettings( bool enabled, QgsSnappingConfig::SnappingType type, double tolerance, QgsTolerance::UnitType units, bool avoidIntersection = false );
|
||||
|
||||
/**
|
||||
* Constructs an invalid setting
|
||||
*/
|
||||
IndividualLayerSettings();
|
||||
|
||||
//! return if settings are valid
|
||||
bool valid() const;
|
||||
|
||||
//! return if snaping is enbaled
|
||||
bool enabled() const;
|
||||
|
||||
//! enables the snapping
|
||||
void setEnabled( bool enabled );
|
||||
|
||||
//! return the type (vertices and/or segments)
|
||||
QgsSnappingConfig::SnappingType type() const;
|
||||
|
||||
//! define the type of snapping
|
||||
void setType( QgsSnappingConfig::SnappingType type );
|
||||
|
||||
//! return the tolerance
|
||||
double tolerance() const;
|
||||
|
||||
//! set the tolerance
|
||||
void setTolerance( double tolerance );
|
||||
|
||||
//! return the type of units
|
||||
QgsTolerance::UnitType units() const;
|
||||
|
||||
//! set the type of units
|
||||
void setUnits( QgsTolerance::UnitType units );
|
||||
|
||||
//! return if it shall avoid intersection (polygon layers only)
|
||||
bool avoidIntersection() const;
|
||||
|
||||
//! set if it shall avoid intersection (polygon layers only)
|
||||
void setAvoidIntersection( bool avoidIntersection );
|
||||
|
||||
/**
|
||||
* Compare this configuration to other.
|
||||
*/
|
||||
bool operator!= ( const IndividualLayerSettings& other ) const;
|
||||
|
||||
bool operator== ( const IndividualLayerSettings& other ) const;
|
||||
|
||||
private:
|
||||
bool mValid;
|
||||
bool mEnabled;
|
||||
SnappingType mType;
|
||||
double mTolerance;
|
||||
QgsTolerance::UnitType mUnits;
|
||||
bool mAvoidIntersection;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor with default parameters defined in global settings
|
||||
*/
|
||||
explicit QgsSnappingConfig();
|
||||
|
||||
~QgsSnappingConfig();
|
||||
|
||||
bool operator==( const QgsSnappingConfig& other ) const;
|
||||
|
||||
//! reset to default values
|
||||
void reset();
|
||||
|
||||
//! return if snapping is enbaled
|
||||
bool enabled() const;
|
||||
|
||||
//! enables the snapping
|
||||
void setEnabled( bool enabled );
|
||||
|
||||
//! return the mode (all layers, active layer, per layer settings)
|
||||
SnappingMode mode() const;
|
||||
|
||||
//! define the mode of snapping
|
||||
void setMode( SnappingMode mode );
|
||||
|
||||
//! return the type (vertices and/or segments)
|
||||
SnappingType type() const;
|
||||
|
||||
//! define the type of snapping
|
||||
void setType( SnappingType type );
|
||||
|
||||
//! return the tolerance
|
||||
double tolerance() const;
|
||||
|
||||
//! set the tolerance
|
||||
void setTolerance( double tolerance );
|
||||
|
||||
//! return the type of units
|
||||
QgsTolerance::UnitType units() const;
|
||||
|
||||
//! set the type of units
|
||||
void setUnits( QgsTolerance::UnitType units );
|
||||
|
||||
//! return if the topological editing is enabled
|
||||
bool topologicalEditing() const;
|
||||
|
||||
//! set if the topological editing is enabled
|
||||
void setTopologicalEditing( bool enabled );
|
||||
|
||||
//! return if the snapping on intersection is enabled
|
||||
bool intersectionSnapping() const;
|
||||
|
||||
//! set if the snapping on intersection is enabled
|
||||
void setIntersectionSnapping( bool enabled );
|
||||
|
||||
//! return individual snapping settings for all layers
|
||||
QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> individualLayerSettings() const;
|
||||
|
||||
//! return individual layer snappings settings (applied if mode is AdvancedConfiguration)
|
||||
QgsSnappingConfig::IndividualLayerSettings individualLayerSettings( QgsVectorLayer* vl ) const;
|
||||
|
||||
//! set individual layer snappings settings (applied if mode is AdvancedConfiguration)
|
||||
void setIndividualLayerSettings( QgsVectorLayer* vl, QgsSnappingConfig::IndividualLayerSettings individualLayerSettings );
|
||||
|
||||
/**
|
||||
* Compare this configuration to other.
|
||||
*/
|
||||
bool operator!= ( const QgsSnappingConfig& other ) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Reads the configuration from the specified QGIS project document.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
void readProject( const QDomDocument& doc );
|
||||
|
||||
/**
|
||||
* Writes the configuration to the specified QGIS project document.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
void writeProject( QDomDocument& doc );
|
||||
|
||||
/**
|
||||
* Adds the specified layers as individual layers to the configuration
|
||||
* with standard configuration.
|
||||
* When implementing a long-living QgsSnappingConfig (like the one in QgsProject)
|
||||
* it is best to directly feed this with information from the layer registry.
|
||||
*
|
||||
* @return True if changes have been done.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
bool addLayers( const QList<QgsMapLayer*>& layers );
|
||||
|
||||
|
||||
/**
|
||||
* Removes the specified layers from the individual layer configuration.
|
||||
* When implementing a long-living QgsSnappingConfig (like the one in QgsProject)
|
||||
* it is best to directly feed this with information from the layer registry.
|
||||
*
|
||||
* @return True if changes have been done.
|
||||
*
|
||||
* @note Added in QGIS 3.0
|
||||
*/
|
||||
bool removeLayers( const QList<QgsMapLayer*>& layers );
|
||||
|
||||
private:
|
||||
void readLegacySettings();
|
||||
|
||||
bool mEnabled;
|
||||
SnappingMode mMode;
|
||||
SnappingType mType;
|
||||
double mTolerance;
|
||||
QgsTolerance::UnitType mUnits;
|
||||
bool mTopologicalEditing;
|
||||
bool mIntersectionSnapping;
|
||||
|
||||
QHash<QgsVectorLayer*, IndividualLayerSettings> mIndividualLayerSettings;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSPROJECTSNAPPINGSETTINGS_H
|
@ -24,16 +24,10 @@
|
||||
QgsSnappingUtils::QgsSnappingUtils( QObject* parent )
|
||||
: QObject( parent )
|
||||
, mCurrentLayer( nullptr )
|
||||
, mSnapToMapMode( SnapCurrentLayer )
|
||||
, mStrategy( IndexHybrid )
|
||||
, mDefaultType( QgsPointLocator::Vertex )
|
||||
, mDefaultTolerance( 10 )
|
||||
, mDefaultUnit( QgsTolerance::Pixels )
|
||||
, mSnapOnIntersection( false )
|
||||
, mHybridPerLayerFeatureLimit( 50000 )
|
||||
, mIsIndexing( false )
|
||||
{
|
||||
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QStringList ) ), this, SLOT( onLayersWillBeRemoved( QStringList ) ) );
|
||||
}
|
||||
|
||||
QgsSnappingUtils::~QgsSnappingUtils()
|
||||
@ -57,12 +51,10 @@ QgsPointLocator* QgsSnappingUtils::locatorForLayer( QgsVectorLayer* vl )
|
||||
|
||||
void QgsSnappingUtils::clearAllLocators()
|
||||
{
|
||||
Q_FOREACH ( QgsPointLocator* vlpl, mLocators )
|
||||
delete vlpl;
|
||||
qDeleteAll( mLocators );
|
||||
mLocators.clear();
|
||||
|
||||
Q_FOREACH ( QgsPointLocator* vlpl, mTemporaryLocators )
|
||||
delete vlpl;
|
||||
qDeleteAll( mTemporaryLocators );
|
||||
mTemporaryLocators.clear();
|
||||
}
|
||||
|
||||
@ -214,17 +206,17 @@ inline QgsRectangle _areaOfInterest( const QgsPoint& point, double tolerance )
|
||||
|
||||
QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, QgsPointLocator::MatchFilter* filter )
|
||||
{
|
||||
if ( !mMapSettings.hasValidSettings() )
|
||||
if ( !mMapSettings.hasValidSettings() || !mSnappingConfig.enabled() )
|
||||
return QgsPointLocator::Match();
|
||||
|
||||
if ( mSnapToMapMode == SnapCurrentLayer )
|
||||
if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
|
||||
{
|
||||
if ( !mCurrentLayer || mDefaultType == 0 )
|
||||
if ( !mCurrentLayer || mSnappingConfig.type() == 0 )
|
||||
return QgsPointLocator::Match();
|
||||
|
||||
// data from project
|
||||
double tolerance = QgsTolerance::toleranceInProjectUnits( mDefaultTolerance, mCurrentLayer, mMapSettings, mDefaultUnit );
|
||||
int type = mDefaultType;
|
||||
double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), mCurrentLayer, mMapSettings, mSnappingConfig.units() );
|
||||
int type = mSnappingConfig.type();
|
||||
|
||||
prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer, _areaOfInterest( pointMap, tolerance ) ) );
|
||||
|
||||
@ -236,7 +228,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
|
||||
QgsPointLocator::Match bestMatch;
|
||||
_updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
|
||||
|
||||
if ( mSnapOnIntersection )
|
||||
if ( mSnappingConfig.intersectionSnapping() )
|
||||
{
|
||||
QgsPointLocator* locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
|
||||
QgsPointLocator::MatchList edges = locEdges->edgesInRect( pointMap, tolerance );
|
||||
@ -245,7 +237,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
else if ( mSnapToMapMode == SnapAdvanced )
|
||||
else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
|
||||
{
|
||||
QList<LayerAndAreaOfInterest> layers;
|
||||
Q_FOREACH ( const LayerConfig& layerConfig, mLayers )
|
||||
@ -266,7 +258,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
|
||||
{
|
||||
_updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter );
|
||||
|
||||
if ( mSnapOnIntersection )
|
||||
if ( mSnappingConfig.intersectionSnapping() )
|
||||
{
|
||||
edges << loc->edgesInRect( pointMap, tolerance );
|
||||
maxSnapIntTolerance = qMax( maxSnapIntTolerance, tolerance );
|
||||
@ -274,16 +266,16 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
|
||||
}
|
||||
}
|
||||
|
||||
if ( mSnapOnIntersection )
|
||||
if ( mSnappingConfig.intersectionSnapping() )
|
||||
_replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxSnapIntTolerance );
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
else if ( mSnapToMapMode == SnapAllLayers )
|
||||
else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
|
||||
{
|
||||
// data from project
|
||||
double tolerance = QgsTolerance::toleranceInProjectUnits( mDefaultTolerance, nullptr, mMapSettings, mDefaultUnit );
|
||||
int type = mDefaultType;
|
||||
double tolerance = QgsTolerance::toleranceInProjectUnits( mSnappingConfig.tolerance(), nullptr, mMapSettings, mSnappingConfig.units() );
|
||||
int type = mSnappingConfig.type();
|
||||
QgsRectangle aoi = _areaOfInterest( pointMap, tolerance );
|
||||
|
||||
QList<LayerAndAreaOfInterest> layers;
|
||||
@ -302,12 +294,12 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
|
||||
{
|
||||
_updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
|
||||
|
||||
if ( mSnapOnIntersection )
|
||||
if ( mSnappingConfig.intersectionSnapping() )
|
||||
edges << loc->edgesInRect( pointMap, tolerance );
|
||||
}
|
||||
}
|
||||
|
||||
if ( mSnapOnIntersection )
|
||||
if ( mSnappingConfig.intersectionSnapping() )
|
||||
_replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
|
||||
|
||||
return bestMatch;
|
||||
@ -404,6 +396,22 @@ void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest>& layers
|
||||
mIsIndexing = false;
|
||||
}
|
||||
|
||||
QgsSnappingConfig QgsSnappingUtils::config() const
|
||||
{
|
||||
return mSnappingConfig;
|
||||
}
|
||||
|
||||
void QgsSnappingUtils::setConfig( const QgsSnappingConfig& config )
|
||||
{
|
||||
if ( mSnappingConfig == config )
|
||||
return;
|
||||
|
||||
if ( mSnappingConfig.individualLayerSettings() != config.individualLayerSettings() )
|
||||
onIndividualLayerSettingsChanged( config.individualLayerSettings() );
|
||||
|
||||
mSnappingConfig = config;
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
QgsPointLocator::Match QgsSnappingUtils::snapToCurrentLayer( QPoint point, int type, QgsPointLocator::MatchFilter* filter )
|
||||
{
|
||||
@ -437,58 +445,6 @@ void QgsSnappingUtils::setCurrentLayer( QgsVectorLayer* layer )
|
||||
mCurrentLayer = layer;
|
||||
}
|
||||
|
||||
void QgsSnappingUtils::setSnapToMapMode( QgsSnappingUtils::SnapToMapMode mode )
|
||||
{
|
||||
if ( mSnapToMapMode == mode )
|
||||
return;
|
||||
|
||||
mSnapToMapMode = mode;
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
void QgsSnappingUtils::setDefaultSettings( int type, double tolerance, QgsTolerance::UnitType unit )
|
||||
{
|
||||
// force map units - can't use layer units for just any layer
|
||||
if ( unit == QgsTolerance::LayerUnits )
|
||||
unit = QgsTolerance::ProjectUnits;
|
||||
|
||||
if ( mDefaultType == type && mDefaultTolerance == tolerance && mDefaultUnit == unit )
|
||||
return;
|
||||
|
||||
mDefaultType = type;
|
||||
mDefaultTolerance = tolerance;
|
||||
mDefaultUnit = unit;
|
||||
|
||||
if ( mSnapToMapMode != SnapAdvanced ) // does not affect advanced mode
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
void QgsSnappingUtils::defaultSettings( int& type, double& tolerance, QgsTolerance::UnitType& unit )
|
||||
{
|
||||
type = mDefaultType;
|
||||
tolerance = mDefaultTolerance;
|
||||
unit = mDefaultUnit;
|
||||
}
|
||||
|
||||
void QgsSnappingUtils::setLayers( const QList<QgsSnappingUtils::LayerConfig>& layers )
|
||||
{
|
||||
if ( mLayers == layers )
|
||||
return;
|
||||
|
||||
mLayers = layers;
|
||||
if ( mSnapToMapMode == SnapAdvanced ) // only affects advanced mode
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
void QgsSnappingUtils::setSnapOnIntersections( bool enabled )
|
||||
{
|
||||
if ( mSnapOnIntersection == enabled )
|
||||
return;
|
||||
|
||||
mSnapOnIntersection = enabled;
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
QString QgsSnappingUtils::dump()
|
||||
{
|
||||
QString msg = "--- SNAPPING UTILS DUMP ---\n";
|
||||
@ -501,25 +457,25 @@ QString QgsSnappingUtils::dump()
|
||||
|
||||
QList<LayerConfig> layers;
|
||||
|
||||
if ( mSnapToMapMode == SnapCurrentLayer )
|
||||
if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer )
|
||||
{
|
||||
if ( mSnapToMapMode == SnapCurrentLayer && !mCurrentLayer )
|
||||
if ( mSnappingConfig.mode() == QgsSnappingConfig::ActiveLayer && !mCurrentLayer )
|
||||
{
|
||||
msg += "no current layer!";
|
||||
return msg;
|
||||
}
|
||||
|
||||
layers << LayerConfig( mCurrentLayer, QgsPointLocator::Types( mDefaultType ), mDefaultTolerance, mDefaultUnit );
|
||||
layers << LayerConfig( mCurrentLayer, QgsPointLocator::Types( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
|
||||
}
|
||||
else if ( mSnapToMapMode == SnapAllLayers )
|
||||
else if ( mSnappingConfig.mode() == QgsSnappingConfig::AllLayers )
|
||||
{
|
||||
Q_FOREACH ( const QString& layerID, mMapSettings.layers() )
|
||||
{
|
||||
if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) ) )
|
||||
layers << LayerConfig( vl, QgsPointLocator::Types( mDefaultType ), mDefaultTolerance, mDefaultUnit );
|
||||
layers << LayerConfig( vl, QgsPointLocator::Types( mSnappingConfig.type() ), mSnappingConfig.tolerance(), mSnappingConfig.units() );
|
||||
}
|
||||
}
|
||||
else if ( mSnapToMapMode == SnapAdvanced )
|
||||
else if ( mSnappingConfig.mode() == QgsSnappingConfig::AdvancedConfiguration )
|
||||
{
|
||||
layers = mLayers;
|
||||
}
|
||||
@ -575,117 +531,23 @@ QgsCoordinateReferenceSystem QgsSnappingUtils::destinationCrs() const
|
||||
return mMapSettings.hasCrsTransformEnabled() ? mMapSettings.destinationCrs() : QgsCoordinateReferenceSystem();
|
||||
}
|
||||
|
||||
|
||||
void QgsSnappingUtils::readConfigFromProject()
|
||||
void QgsSnappingUtils::onIndividualLayerSettingsChanged( const QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> layerSettings )
|
||||
{
|
||||
mSnapToMapMode = SnapCurrentLayer;
|
||||
mLayers.clear();
|
||||
|
||||
QString snapMode = QgsProject::instance()->readEntry( "Digitizing", "/SnappingMode" );
|
||||
QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
|
||||
|
||||
int type = 0;
|
||||
QString snapType = QgsProject::instance()->readEntry( "Digitizing", "/DefaultSnapType", QString( "off" ) );
|
||||
if ( snapType == "to segment" )
|
||||
type = QgsPointLocator::Edge;
|
||||
else if ( snapType == "to vertex and segment" )
|
||||
type = QgsPointLocator::Vertex | QgsPointLocator::Edge;
|
||||
else if ( snapType == "to vertex" )
|
||||
type = QgsPointLocator::Vertex;
|
||||
double tolerance = QgsProject::instance()->readDoubleEntry( "Digitizing", "/DefaultSnapTolerance", 0 );
|
||||
QgsTolerance::UnitType unit = static_cast< QgsTolerance::UnitType >( QgsProject::instance()->readNumEntry( "Digitizing", "/DefaultSnapToleranceUnit", QgsTolerance::ProjectUnits ) );
|
||||
setDefaultSettings( type, tolerance, unit );
|
||||
|
||||
//snapping on intersection on?
|
||||
setSnapOnIntersections( QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 ) );
|
||||
|
||||
//read snapping settings from project
|
||||
bool snappingDefinedInProject, ok;
|
||||
QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &snappingDefinedInProject );
|
||||
QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok );
|
||||
QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok );
|
||||
QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok );
|
||||
QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok );
|
||||
|
||||
// lists must have the same size, otherwise something is wrong
|
||||
if ( layerIdList.size() != enabledList.size() ||
|
||||
layerIdList.size() != toleranceList.size() ||
|
||||
layerIdList.size() != toleranceUnitList.size() ||
|
||||
layerIdList.size() != snapToList.size() )
|
||||
return;
|
||||
|
||||
if ( !snappingDefinedInProject )
|
||||
return; // nothing defined in project - use current layer
|
||||
|
||||
// Use snapping information from the project
|
||||
if ( snapMode == "current_layer" )
|
||||
mSnapToMapMode = SnapCurrentLayer;
|
||||
else if ( snapMode == "all_layers" )
|
||||
mSnapToMapMode = SnapAllLayers;
|
||||
else // either "advanced" or empty (for background compatibility)
|
||||
mSnapToMapMode = SnapAdvanced;
|
||||
|
||||
|
||||
|
||||
// load layers, tolerances, snap type
|
||||
QStringList::const_iterator layerIt( layerIdList.constBegin() );
|
||||
QStringList::const_iterator tolIt( toleranceList.constBegin() );
|
||||
QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
|
||||
QStringList::const_iterator snapIt( snapToList.constBegin() );
|
||||
QStringList::const_iterator enabledIt( enabledList.constBegin() );
|
||||
for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
|
||||
for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
|
||||
{
|
||||
// skip layer if snapping is not enabled
|
||||
if ( *enabledIt != "enabled" )
|
||||
continue;
|
||||
|
||||
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) );
|
||||
if ( !vlayer || !vlayer->hasGeometryType() )
|
||||
continue;
|
||||
|
||||
QgsPointLocator::Types t( *snapIt == "to_vertex" ? QgsPointLocator::Vertex :
|
||||
( *snapIt == "to_segment" ? QgsPointLocator::Edge :
|
||||
QgsPointLocator::Vertex | QgsPointLocator::Edge
|
||||
)
|
||||
);
|
||||
mLayers.append( LayerConfig( vlayer, t, tolIt->toDouble(), static_cast< QgsTolerance::UnitType >( tolUnitIt->toInt() ) ) );
|
||||
}
|
||||
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
void QgsSnappingUtils::onLayersWillBeRemoved( const QStringList& layerIds )
|
||||
{
|
||||
// remove locators for layers that are going to be deleted
|
||||
Q_FOREACH ( const QString& layerId, layerIds )
|
||||
{
|
||||
if ( mHybridMaxAreaPerLayer.contains( layerId ) )
|
||||
mHybridMaxAreaPerLayer.remove( layerId );
|
||||
|
||||
for ( LocatorsMap::iterator it = mLocators.begin(); it != mLocators.end(); )
|
||||
if ( i->enabled() )
|
||||
{
|
||||
if ( it.key()->id() == layerId )
|
||||
{
|
||||
delete it.value();
|
||||
it = mLocators.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
QgsPointLocator::Types t( i->type() == QgsSnappingConfig::Vertex ? QgsPointLocator::Vertex :
|
||||
( i->type() == QgsSnappingConfig::Segment ? QgsPointLocator::Edge :
|
||||
QgsPointLocator::Vertex | QgsPointLocator::Edge
|
||||
)
|
||||
);
|
||||
|
||||
for ( LocatorsMap::iterator it = mTemporaryLocators.begin(); it != mTemporaryLocators.end(); )
|
||||
{
|
||||
if ( it.key()->id() == layerId )
|
||||
{
|
||||
delete it.value();
|
||||
it = mTemporaryLocators.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
mLayers.append( LayerConfig( i.key(), t, i->tolerance(), i->units() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,9 @@
|
||||
#include "qgsmapsettings.h"
|
||||
#include "qgstolerance.h"
|
||||
#include "qgspointlocator.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
class QgsSnappingConfig;
|
||||
|
||||
/** \ingroup core
|
||||
* This class has all the configuration of snapping and can return answers to snapping queries.
|
||||
@ -41,6 +44,9 @@
|
||||
class CORE_EXPORT QgsSnappingUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( QgsSnappingConfig config READ config WRITE setConfig NOTIFY configChanged )
|
||||
|
||||
public:
|
||||
QgsSnappingUtils( QObject* parent = nullptr );
|
||||
~QgsSnappingUtils();
|
||||
@ -61,7 +67,7 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
|
||||
|
||||
/** Assign current map settings to the utils - used for conversion between screen coords to map coords */
|
||||
void setMapSettings( const QgsMapSettings& settings );
|
||||
const QgsMapSettings& mapSettings() const { return mMapSettings; }
|
||||
QgsMapSettings mapSettings() const { return mMapSettings; }
|
||||
|
||||
/** Set current layer so that if mode is SnapCurrentLayer we know which layer to use */
|
||||
void setCurrentLayer( QgsVectorLayer* layer );
|
||||
@ -79,11 +85,6 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
|
||||
SnapAdvanced, //!< snap according to the configuration set in setLayers()
|
||||
};
|
||||
|
||||
/** Set how the snapping to map is done */
|
||||
void setSnapToMapMode( SnapToMapMode mode );
|
||||
/** Find out how the snapping to map is done */
|
||||
SnapToMapMode snapToMapMode() const { return mSnapToMapMode; }
|
||||
|
||||
enum IndexingStrategy
|
||||
{
|
||||
IndexAlwaysFull, //!< For all layers build index of full extent. Uses more memory, but queries are faster.
|
||||
@ -96,11 +97,6 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
|
||||
/** Find out which strategy is used for indexing - by default hybrid indexing is used */
|
||||
IndexingStrategy indexingStrategy() const { return mStrategy; }
|
||||
|
||||
/** Configure options used when the mode is snap to current layer or to all layers */
|
||||
void setDefaultSettings( int type, double tolerance, QgsTolerance::UnitType unit );
|
||||
/** Query options used when the mode is snap to current layer or to all layers */
|
||||
void defaultSettings( int& type, double& tolerance, QgsTolerance::UnitType& unit );
|
||||
|
||||
/**
|
||||
* Configures how a certain layer should be handled in a snapping operation
|
||||
*/
|
||||
@ -150,28 +146,27 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
|
||||
QgsTolerance::UnitType unit;
|
||||
};
|
||||
|
||||
/** Set layers which will be used for snapping */
|
||||
void setLayers( const QList<LayerConfig>& layers );
|
||||
/** Query layers used for snapping */
|
||||
QList<LayerConfig> layers() const { return mLayers; }
|
||||
|
||||
/** Set whether to consider intersections of nearby segments for snapping */
|
||||
void setSnapOnIntersections( bool enabled );
|
||||
/** Query whether to consider intersections of nearby segments for snapping */
|
||||
bool snapOnIntersections() const { return mSnapOnIntersection; }
|
||||
|
||||
/** Get extra information about the instance
|
||||
* @note added in QGIS 2.14
|
||||
*/
|
||||
QString dump();
|
||||
|
||||
public slots:
|
||||
/** Read snapping configuration from the project */
|
||||
void readConfigFromProject();
|
||||
/**
|
||||
* The snapping configuration controls the behavior of this object
|
||||
*/
|
||||
QgsSnappingConfig config() const;
|
||||
|
||||
/**
|
||||
* The snapping configuration controls the behavior of this object
|
||||
*/
|
||||
void setConfig( const QgsSnappingConfig& snappingConfig );
|
||||
|
||||
signals:
|
||||
/** Emitted when snapping configuration has been changed
|
||||
* @note added in QGIS 2.14
|
||||
/**
|
||||
* Emitted when the snapping settings object changes.
|
||||
*/
|
||||
void configChanged();
|
||||
|
||||
@ -181,10 +176,8 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
|
||||
//! Called when finished indexing a layer. When index == count the indexing is complete
|
||||
virtual void prepareIndexProgress( int index ) { Q_UNUSED( index ); }
|
||||
|
||||
private slots:
|
||||
void onLayersWillBeRemoved( const QStringList& layerIds );
|
||||
|
||||
private:
|
||||
void onIndividualLayerSettingsChanged( const QHash<QgsVectorLayer*, QgsSnappingConfig::IndividualLayerSettings> layerSettings );
|
||||
//! Get destination CRS from map settings, or an invalid CRS if projections are disabled
|
||||
QgsCoordinateReferenceSystem destinationCrs() const;
|
||||
|
||||
@ -208,14 +201,11 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
|
||||
QgsMapSettings mMapSettings;
|
||||
QgsVectorLayer* mCurrentLayer;
|
||||
|
||||
QgsSnappingConfig mSnappingConfig;
|
||||
|
||||
// configuration
|
||||
SnapToMapMode mSnapToMapMode;
|
||||
IndexingStrategy mStrategy;
|
||||
int mDefaultType;
|
||||
double mDefaultTolerance;
|
||||
QgsTolerance::UnitType mDefaultUnit;
|
||||
QList<LayerConfig> mLayers;
|
||||
bool mSnapOnIntersection;
|
||||
|
||||
// internal data
|
||||
typedef QMap<QgsVectorLayer*, QgsPointLocator*> LocatorsMap;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "qgsmessagebaritem.h"
|
||||
#include "qgssnappingutils.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
@ -101,17 +102,17 @@ void QgsMapCanvasTracer::configure()
|
||||
QList<QgsVectorLayer*> layers;
|
||||
QStringList visibleLayerIds = mCanvas->mapSettings().layers();
|
||||
|
||||
switch ( mCanvas->snappingUtils()->snapToMapMode() )
|
||||
switch ( mCanvas->snappingUtils()->config().mode() )
|
||||
{
|
||||
default:
|
||||
case QgsSnappingUtils::SnapCurrentLayer:
|
||||
case QgsSnappingConfig::ActiveLayer:
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( mCanvas->currentLayer() );
|
||||
if ( vl && visibleLayerIds.contains( vl->id() ) )
|
||||
layers << vl;
|
||||
}
|
||||
break;
|
||||
case QgsSnappingUtils::SnapAllLayers:
|
||||
case QgsSnappingConfig::AllLayers:
|
||||
Q_FOREACH ( const QString& layerId, visibleLayerIds )
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerId ) );
|
||||
@ -119,7 +120,7 @@ void QgsMapCanvasTracer::configure()
|
||||
layers << vl;
|
||||
}
|
||||
break;
|
||||
case QgsSnappingUtils::SnapAdvanced:
|
||||
case QgsSnappingConfig::AdvancedConfiguration:
|
||||
Q_FOREACH ( const QgsSnappingUtils::LayerConfig& cfg, mCanvas->snappingUtils()->layers() )
|
||||
{
|
||||
if ( visibleLayerIds.contains( cfg.layer->id() ) )
|
||||
@ -134,6 +135,6 @@ void QgsMapCanvasTracer::configure()
|
||||
void QgsMapCanvasTracer::onCurrentLayerChanged()
|
||||
{
|
||||
// no need to bother if we are not snapping
|
||||
if ( mCanvas->snappingUtils()->snapToMapMode() == QgsSnappingUtils::SnapCurrentLayer )
|
||||
if ( mCanvas->snappingUtils()->config().mode() == QgsSnappingConfig::ActiveLayer )
|
||||
invalidateGraph();
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "qgsmapcanvas.h"
|
||||
|
||||
#include "qgssnappingutils.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
/// @cond PRIVATE
|
||||
struct EdgesOnlyFilter : public QgsPointLocator::MatchFilter
|
||||
@ -62,18 +63,18 @@ QgsPoint QgsMapMouseEvent::snapPoint( SnappingMode snappingMode )
|
||||
}
|
||||
|
||||
QgsSnappingUtils* snappingUtils = mMapCanvas->snappingUtils();
|
||||
QgsSnappingUtils::SnapToMapMode canvasMode = snappingUtils->snapToMapMode();
|
||||
if ( snappingMode == SnapAllLayers )
|
||||
{
|
||||
int type;
|
||||
double tolerance;
|
||||
QgsTolerance::UnitType unit;
|
||||
snappingUtils->defaultSettings( type, tolerance, unit );
|
||||
snappingUtils->setSnapToMapMode( QgsSnappingUtils::SnapAllLayers );
|
||||
snappingUtils->setDefaultSettings( QgsPointLocator::Vertex | QgsPointLocator::Edge, tolerance, unit );
|
||||
QgsSnappingConfig canvasConfig = snappingUtils->config();
|
||||
QgsSnappingConfig localConfig = snappingUtils->config();
|
||||
|
||||
localConfig.setMode( QgsSnappingConfig::AllLayers );
|
||||
localConfig.setType( QgsSnappingConfig::VertexAndSegment );
|
||||
snappingUtils->setConfig( localConfig );
|
||||
|
||||
mSnapMatch = snappingUtils->snapToMap( mMapPoint );
|
||||
snappingUtils->setSnapToMapMode( canvasMode );
|
||||
snappingUtils->setDefaultSettings( type, tolerance, unit );
|
||||
|
||||
snappingUtils->setConfig( canvasConfig );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -119,16 +120,17 @@ QList<QgsPoint> QgsMapMouseEvent::snapSegment( SnappingMode snappingMode, bool*
|
||||
{
|
||||
// run snapToMap with only edges on all layers
|
||||
QgsSnappingUtils* snappingUtils = mMapCanvas->snappingUtils();
|
||||
QgsSnappingUtils::SnapToMapMode canvasMode = snappingUtils->snapToMapMode();
|
||||
int type;
|
||||
double tolerance;
|
||||
QgsTolerance::UnitType unit;
|
||||
snappingUtils->defaultSettings( type, tolerance, unit );
|
||||
snappingUtils->setSnapToMapMode( QgsSnappingUtils::SnapAllLayers );
|
||||
snappingUtils->setDefaultSettings( QgsPointLocator::Edge, tolerance, unit );
|
||||
|
||||
QgsSnappingConfig canvasConfig = snappingUtils->config();
|
||||
QgsSnappingConfig localConfig = snappingUtils->config();
|
||||
|
||||
localConfig.setMode( QgsSnappingConfig::AllLayers );
|
||||
localConfig.setType( QgsSnappingConfig::Segment );
|
||||
snappingUtils->setConfig( localConfig );
|
||||
|
||||
match = snappingUtils->snapToMap( mOriginalMapPoint );
|
||||
snappingUtils->setSnapToMapMode( canvasMode );
|
||||
snappingUtils->setDefaultSettings( type, tolerance, unit );
|
||||
|
||||
snappingUtils->setConfig( canvasConfig );
|
||||
}
|
||||
if ( match.isValid() && match.hasEdge() )
|
||||
{
|
||||
|
@ -17,7 +17,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1018</width>
|
||||
<height>25</height>
|
||||
<height>19</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
@ -570,6 +570,17 @@
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="mSnappingToolBar">
|
||||
<property name="windowTitle">
|
||||
<string>Snapping toolbar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<action name="mActionNewProject">
|
||||
<property name="icon">
|
||||
<iconset resource="../../images/images.qrc">
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>791</width>
|
||||
<width>857</width>
|
||||
<height>658</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -308,7 +308,7 @@
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="mOptionsStackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>13</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="mOptionsPageGeneral">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
@ -337,8 +337,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>723</width>
|
||||
<height>640</height>
|
||||
<width>685</width>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_28">
|
||||
@ -1023,8 +1023,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>607</width>
|
||||
<height>850</height>
|
||||
<width>671</width>
|
||||
<height>869</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_22">
|
||||
@ -1456,8 +1456,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>601</width>
|
||||
<height>694</height>
|
||||
<width>671</width>
|
||||
<height>659</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_27">
|
||||
@ -1814,8 +1814,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>747</width>
|
||||
<height>972</height>
|
||||
<width>684</width>
|
||||
<height>924</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_22">
|
||||
@ -2612,8 +2612,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>617</width>
|
||||
<height>607</height>
|
||||
<width>685</width>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_46">
|
||||
@ -2759,8 +2759,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>617</width>
|
||||
<height>607</height>
|
||||
<width>685</width>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_25">
|
||||
@ -3102,7 +3102,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>650</width>
|
||||
<width>685</width>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -3533,8 +3533,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>617</width>
|
||||
<height>607</height>
|
||||
<width>685</width>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_39">
|
||||
@ -3802,8 +3802,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>601</width>
|
||||
<height>668</height>
|
||||
<width>671</width>
|
||||
<height>669</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_31">
|
||||
@ -4003,14 +4003,7 @@
|
||||
<string>Snapping</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="_10">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="mDefaultSnapModeLabel">
|
||||
<property name="text">
|
||||
<string>Default snap mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4" colspan="2">
|
||||
<item row="3" column="5" colspan="2">
|
||||
<widget class="QComboBox" name="mDefaultSnapModeComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -4020,14 +4013,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="mDefaultSnappingToleranceTextLabel">
|
||||
<property name="text">
|
||||
<string>Default snapping tolerance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<item row="4" column="5">
|
||||
<widget class="QDoubleSpinBox" name="mDefaultSnappingToleranceSpinBox">
|
||||
<property name="decimals">
|
||||
<number>5</number>
|
||||
@ -4037,14 +4023,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QLabel" name="mVertexSearchRadiusVertexEditLabel">
|
||||
<property name="text">
|
||||
<string>Search radius for vertex edits</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="4">
|
||||
<item row="5" column="5">
|
||||
<widget class="QDoubleSpinBox" name="mSearchRadiusVertexEditSpinBox">
|
||||
<property name="decimals">
|
||||
<number>5</number>
|
||||
@ -4054,7 +4033,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="5">
|
||||
<item row="4" column="6">
|
||||
<widget class="QComboBox" name="mDefaultSnappingToleranceComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@ -4074,7 +4053,7 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="5">
|
||||
<item row="5" column="6">
|
||||
<widget class="QComboBox" name="mSearchRadiusVertexEditComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
@ -4088,7 +4067,7 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<item row="5" column="4">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -4101,7 +4080,7 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="2" colspan="2">
|
||||
<item row="4" column="3" colspan="2">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -4114,7 +4093,7 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<item row="3" column="2" colspan="3">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -4127,13 +4106,47 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="6">
|
||||
<widget class="QCheckBox" name="cbxSnappingOptionsDocked">
|
||||
<item row="5" column="1" colspan="3">
|
||||
<widget class="QLabel" name="mVertexSearchRadiusVertexEditLabel">
|
||||
<property name="text">
|
||||
<string>Open snapping options in a dock window (QGIS restart required)</string>
|
||||
<string>Search radius for vertex edits</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_49">
|
||||
<property name="text">
|
||||
<string>Display main dialog as (restart required)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="mDefaultSnapModeLabel">
|
||||
<property name="text">
|
||||
<string>Default snap mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Display simplified panel in (restart required)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="QLabel" name="mDefaultSnappingToleranceTextLabel">
|
||||
<property name="text">
|
||||
<string>Default snapping tolerance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="5" colspan="2">
|
||||
<widget class="QComboBox" name="mSnappingSimplePanelComboBox"/>
|
||||
</item>
|
||||
<item row="1" column="5" colspan="2">
|
||||
<widget class="QComboBox" name="mSnappingMainDialogComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -4347,8 +4360,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>617</width>
|
||||
<height>607</height>
|
||||
<width>685</width>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
@ -4486,8 +4499,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>601</width>
|
||||
<height>644</height>
|
||||
<width>671</width>
|
||||
<height>638</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_15">
|
||||
@ -4732,8 +4745,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>617</width>
|
||||
<height>607</height>
|
||||
<width>685</width>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_32">
|
||||
@ -4841,8 +4854,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>601</width>
|
||||
<height>737</height>
|
||||
<width>671</width>
|
||||
<height>689</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_33">
|
||||
@ -5540,7 +5553,6 @@
|
||||
<tabstop>mLineColorToolButton</tabstop>
|
||||
<tabstop>mFillColorToolButton</tabstop>
|
||||
<tabstop>mLineGhostCheckBox</tabstop>
|
||||
<tabstop>cbxSnappingOptionsDocked</tabstop>
|
||||
<tabstop>mDefaultSnapModeComboBox</tabstop>
|
||||
<tabstop>mDefaultSnappingToleranceSpinBox</tabstop>
|
||||
<tabstop>mDefaultSnappingToleranceComboBox</tabstop>
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgsmaplayerregistry.h"
|
||||
#include "qgssnappingutils.h"
|
||||
#include "qgssnappingconfig.h"
|
||||
|
||||
|
||||
struct FilterExcludePoint : public QgsPointLocator::MatchFilter
|
||||
@ -93,14 +94,21 @@ class TestQgsSnappingUtils : public QObject
|
||||
u.setCurrentLayer( mVL );
|
||||
|
||||
// first try with no snapping enabled
|
||||
u.setDefaultSettings( 0, 10, QgsTolerance::Pixels );
|
||||
QgsSnappingConfig snappingConfig = u.config();
|
||||
snappingConfig.setEnabled( false );
|
||||
snappingConfig.setTolerance( 10 );
|
||||
snappingConfig.setUnits( QgsTolerance::Pixels );
|
||||
snappingConfig.setMode( QgsSnappingConfig::ActiveLayer );
|
||||
u.setConfig( snappingConfig );
|
||||
|
||||
QgsPointLocator::Match m0 = u.snapToMap( QPoint( 100, 100 ) );
|
||||
QVERIFY( !m0.isValid() );
|
||||
QVERIFY( !m0.hasVertex() );
|
||||
|
||||
// now enable snapping
|
||||
u.setDefaultSettings( QgsPointLocator::Vertex | QgsPointLocator::Edge, 10, QgsTolerance::Pixels );
|
||||
snappingConfig.setEnabled( true );
|
||||
snappingConfig.setType( QgsSnappingConfig::Vertex );
|
||||
u.setConfig( snappingConfig );
|
||||
|
||||
QgsPointLocator::Match m = u.snapToMap( QPoint( 100, 100 ) );
|
||||
QVERIFY( m.isValid() );
|
||||
@ -113,7 +121,9 @@ class TestQgsSnappingUtils : public QObject
|
||||
|
||||
// do not consider edges in the following test - on 32-bit platforms
|
||||
// result was an edge match very close to (1,0) instead of being exactly (1,0)
|
||||
u.setDefaultSettings( QgsPointLocator::Vertex, 10, QgsTolerance::Pixels );
|
||||
|
||||
snappingConfig.setType( QgsSnappingConfig::Vertex );
|
||||
u.setConfig( snappingConfig );
|
||||
|
||||
// test with filtering
|
||||
FilterExcludePoint myFilter( QgsPoint( 1, 0 ) );
|
||||
@ -129,8 +139,10 @@ class TestQgsSnappingUtils : public QObject
|
||||
QVERIFY( mapSettings.hasValidSettings() );
|
||||
|
||||
QgsSnappingUtils u;
|
||||
QgsSnappingConfig snappingConfig = u.config();
|
||||
u.setMapSettings( mapSettings );
|
||||
u.setSnapToMapMode( QgsSnappingUtils::SnapAllLayers );
|
||||
snappingConfig.setMode( QgsSnappingConfig::AllLayers );
|
||||
u.setConfig( snappingConfig );
|
||||
|
||||
// right now there are no layers in map settings - snapping will fail
|
||||
|
||||
@ -154,11 +166,11 @@ class TestQgsSnappingUtils : public QObject
|
||||
QVERIFY( mapSettings.hasValidSettings() );
|
||||
|
||||
QgsSnappingUtils u;
|
||||
QgsSnappingConfig snappingConfig = u.config();
|
||||
u.setMapSettings( mapSettings );
|
||||
u.setSnapToMapMode( QgsSnappingUtils::SnapAdvanced );
|
||||
QList<QgsSnappingUtils::LayerConfig> layers;
|
||||
layers << QgsSnappingUtils::LayerConfig( mVL, QgsPointLocator::Vertex, 10, QgsTolerance::Pixels );
|
||||
u.setLayers( layers );
|
||||
snappingConfig.setMode( QgsSnappingConfig::AdvancedConfiguration );
|
||||
snappingConfig.setIndividualLayerSettings( mVL, QgsSnappingConfig::IndividualLayerSettings( true, QgsSnappingConfig::Vertex, 10, QgsTolerance::Pixels ) );
|
||||
u.setConfig( snappingConfig );
|
||||
|
||||
QgsPointLocator::Match m = u.snapToMap( QPoint( 100, 100 ) );
|
||||
QVERIFY( m.isValid() );
|
||||
@ -201,16 +213,18 @@ class TestQgsSnappingUtils : public QObject
|
||||
|
||||
QgsSnappingUtils u;
|
||||
u.setMapSettings( mapSettings );
|
||||
u.setSnapToMapMode( QgsSnappingUtils::SnapAdvanced );
|
||||
QList<QgsSnappingUtils::LayerConfig> layers;
|
||||
layers << QgsSnappingUtils::LayerConfig( vl, QgsPointLocator::Vertex, 0.1, QgsTolerance::ProjectUnits );
|
||||
u.setLayers( layers );
|
||||
QgsSnappingConfig snappingConfig = u.config();
|
||||
snappingConfig.setMode( QgsSnappingConfig::AdvancedConfiguration );
|
||||
QgsSnappingConfig::IndividualLayerSettings layerSettings( true, QgsSnappingConfig::Vertex, 0.1, QgsTolerance::ProjectUnits );
|
||||
snappingConfig.setIndividualLayerSettings( vl, layerSettings );
|
||||
u.setConfig( snappingConfig );
|
||||
|
||||
// no snapping on intersections by default - should find nothing
|
||||
QgsPointLocator::Match m = u.snapToMap( QgsPoint( 0.45, 0.5 ) );
|
||||
QVERIFY( !m.isValid() );
|
||||
|
||||
u.setSnapOnIntersections( true );
|
||||
snappingConfig.setIntersectionSnapping( true );
|
||||
u.setConfig( snappingConfig );
|
||||
|
||||
QgsPointLocator::Match m2 = u.snapToMap( QgsPoint( 0.45, 0.5 ) );
|
||||
QVERIFY( m2.isValid() );
|
||||
|
@ -19,6 +19,7 @@ from qgis.core import (QgsMapLayerRegistry,
|
||||
QgsVectorLayer,
|
||||
QgsMapSettings,
|
||||
QgsSnappingUtils,
|
||||
QgsSnappingConfig,
|
||||
QgsPointLocator,
|
||||
QgsTolerance,
|
||||
QgsRectangle,
|
||||
@ -111,9 +112,12 @@ class TestLayerDependencies(unittest.TestCase):
|
||||
|
||||
u = QgsSnappingUtils()
|
||||
u.setMapSettings(ms)
|
||||
u.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced)
|
||||
layers = [QgsSnappingUtils.LayerConfig(self.pointsLayer, QgsPointLocator.Vertex, 20, QgsTolerance.Pixels)]
|
||||
u.setLayers(layers)
|
||||
cfg = u.config()
|
||||
cfg.setMode(QgsSnappingConfig.AdvancedConfiguration)
|
||||
cfg.setIndividualLayerSettings(self.pointsLayer,
|
||||
QgsSnappingConfig.IndividualLayerSettings(True,
|
||||
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
||||
u.setConfig(cfg)
|
||||
|
||||
m = u.snapToMap(QPoint(95, 100))
|
||||
self.assertTrue(m.isValid())
|
||||
@ -153,10 +157,10 @@ class TestLayerDependencies(unittest.TestCase):
|
||||
self.pointsLayer.setDependencies([])
|
||||
|
||||
# test chained layer dependencies A -> B -> C
|
||||
layers = [QgsSnappingUtils.LayerConfig(self.pointsLayer, QgsPointLocator.Vertex, 20, QgsTolerance.Pixels),
|
||||
QgsSnappingUtils.LayerConfig(self.pointsLayer2, QgsPointLocator.Vertex, 20, QgsTolerance.Pixels)
|
||||
]
|
||||
u.setLayers(layers)
|
||||
cfg.setIndividualLayerSettings(self.pointsLayer2,
|
||||
QgsSnappingConfig.IndividualLayerSettings(True,
|
||||
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
||||
u.setConfig(cfg)
|
||||
self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())])
|
||||
self.pointsLayer2.setDependencies([QgsMapLayerDependency(self.pointsLayer.id())])
|
||||
# add another line
|
||||
@ -230,11 +234,15 @@ class TestLayerDependencies(unittest.TestCase):
|
||||
|
||||
u = QgsSnappingUtils()
|
||||
u.setMapSettings(ms)
|
||||
u.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced)
|
||||
layers = [QgsSnappingUtils.LayerConfig(self.pointsLayer, QgsPointLocator.Vertex, 20, QgsTolerance.Pixels),
|
||||
QgsSnappingUtils.LayerConfig(self.pointsLayer2, QgsPointLocator.Vertex, 20, QgsTolerance.Pixels)
|
||||
]
|
||||
u.setLayers(layers)
|
||||
cfg = u.config()
|
||||
cfg.setMode(QgsSnappingConfig.AdvancedConfiguration)
|
||||
cfg.setIndividualLayerSettings(self.pointsLayer,
|
||||
QgsSnappingConfig.IndividualLayerSettings(True,
|
||||
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
||||
cfg.setIndividualLayerSettings(self.pointsLayer2,
|
||||
QgsSnappingConfig.IndividualLayerSettings(True,
|
||||
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
||||
u.setConfig(cfg)
|
||||
# add another line
|
||||
f = QgsFeature(self.linesLayer.fields())
|
||||
f.setFeatureId(4)
|
||||
|