Fix snapping on invisible geometry

This commit is contained in:
Loïc Bartoletti 2018-03-20 19:08:04 +01:00
parent 5e33d7d639
commit 66b0e59fb9
14 changed files with 280 additions and 132 deletions

View File

@ -71,6 +71,13 @@ Get extent of the area point locator covers - if null then it caches the whole l
Configure extent - if not null, it will index only that area
.. versionadded:: 2.14
%End
void setRenderContext( const QgsRenderContext &context );
%Docstring
Configure render context - if not null, it will use to index only visible feature
.. versionadded:: 3.2
%End
enum Type
@ -199,7 +206,6 @@ Override of edgesInRect that construct rectangle from a center point and toleran
find out if the point is in any polygons
%End
int cachedGeometryCount() const;
%Docstring
Return how many geometries are cached in the index

View File

@ -35,7 +35,7 @@ which keeps the configuration in sync with map canvas (e.g. current view, active
%End
public:
QgsSnappingUtils( QObject *parent /TransferThis/ = 0 );
QgsSnappingUtils( QObject *parent /TransferThis/ = 0, bool enableSnappingForInvisibleFeature = true );
%Docstring
Constructor for QgsSnappingUtils
%End
@ -139,6 +139,15 @@ Get extra information about the instance
QgsSnappingConfig config() const;
%Docstring
The snapping configuration controls the behavior of this object
%End
void setEnableSnappingForInvisibleFeature( bool enable );
%Docstring
Set if invisible features must be snapped or not.
:param enable: Enable or not this feature
.. versionadded:: 3.2
%End
public slots:

View File

@ -9,6 +9,7 @@
class QgsMapCanvasSnappingUtils : QgsSnappingUtils
{
%Docstring

View File

@ -20,6 +20,9 @@ digitizing\default_snap_type=1
# 2 = Project units
digitizing\default_snapping_tolerance_unit=1
# Snap on invisble feature
digitizing\snap_invisible_feature=false
# Default XYZ tile servers to include
connections-xyz\OpenStreetMap\authcfg=
connections-xyz\OpenStreetMap\password=

View File

@ -971,6 +971,7 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mSnappingMarkerColorButton->setColor( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_color" ), QColor( Qt::magenta ) ).value<QColor>() );
mSnappingTooltipsCheckbox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_tooltip" ), false ).toBool() );
mEnableSnappingOnInvisibleFeatureCheckbox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() );
//vertex marker
mMarkersOnlyForSelectedCheckBox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/marker_only_for_selected" ), true ).toBool() );
@ -1483,6 +1484,7 @@ void QgsOptions::saveOptions()
mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_color" ), mSnappingMarkerColorButton->color() );
mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_tooltip" ), mSnappingTooltipsCheckbox->isChecked() );
mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), mEnableSnappingOnInvisibleFeatureCheckbox->isChecked() );
mSettings->setValue( QStringLiteral( "/qgis/digitizing/marker_only_for_selected" ), mMarkersOnlyForSelectedCheckBox->isChecked() );

View File

@ -331,6 +331,7 @@ bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
emit dataChanged();
emit vlayer->styleChanged();
vlayer->triggerRepaint();

View File

@ -21,6 +21,7 @@
#include "qgswkbptr.h"
#include "qgis.h"
#include "qgslogger.h"
#include "qgsrenderer.h"
#include <SpatialIndex.h>
@ -636,6 +637,9 @@ QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateRefe
connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsPointLocator::destroyIndex );
connect( mLayer, &QgsVectorLayer::rendererChanged, this, &QgsPointLocator::destroyIndex );
connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
connect( mLayer, &QgsVectorLayer::layerModified, this, &QgsPointLocator::destroyIndex );
}
@ -661,6 +665,12 @@ void QgsPointLocator::setExtent( const QgsRectangle *extent )
destroyIndex();
}
void QgsPointLocator::setRenderContext( const QgsRenderContext &context )
{
mContext = std::unique_ptr<QgsRenderContext>( new QgsRenderContext( context ) );
destroyIndex();
}
bool QgsPointLocator::init( int maxFeaturesToIndex )
{
@ -686,6 +696,7 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
QgsFeatureRequest request;
request.setSubsetOfAttributes( QgsAttributeList() );
if ( mExtent )
{
QgsRectangle rect = *mExtent;
@ -704,13 +715,40 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
}
request.setFilterRect( rect );
}
bool filter = false;
std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
QgsRenderContext *ctx = nullptr;
if ( mContext )
{
mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
ctx = mContext.get();
if ( renderer )
{
// setup scale for scale dependent visibility (rule based)
renderer->startRender( *ctx, mLayer->fields() );
filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
request.setSubsetOfAttributes( renderer->usedAttributes( *ctx ), mLayer->fields() );
}
}
QgsFeatureIterator fi = mLayer->getFeatures( request );
int indexedCount = 0;
while ( fi.nextFeature( f ) )
{
if ( !f.hasGeometry() )
continue;
if ( ctx && renderer )
{
ctx->expressionContext().setFeature( f );
if ( filter && !renderer->willRenderFeature( f, *ctx ) )
{
continue;
}
}
if ( mTransform.isValid() )
{
try
@ -761,6 +799,12 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
QgsPointLocator_Stream stream( dataList );
mRTree = RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
leafCapacity, dimension, variant, indexId );
if ( ctx && renderer )
{
renderer->stopRender( *ctx );
renderer.release();
}
return true;
}
@ -832,6 +876,7 @@ void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
delete mGeoms.take( fid );
}
}
void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )

View File

@ -18,12 +18,15 @@
class QgsPointXY;
class QgsVectorLayer;
class QgsFeatureRenderer;
class QgsRenderContext;
#include "qgis_core.h"
#include "qgsfeature.h"
#include "qgspointxy.h"
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include <memory>
class QgsPointLocator_VisitorNearestVertex;
class QgsPointLocator_VisitorNearestEdge;
@ -92,6 +95,12 @@ class CORE_EXPORT QgsPointLocator : public QObject
*/
void setExtent( const QgsRectangle *extent );
/**
* Configure render context - if not null, it will use to index only visible feature
* \since QGIS 3.2
*/
void setRenderContext( const QgsRenderContext &context );
/**
* The type of a snap result or the filter type for a snap request.
*/
@ -251,8 +260,6 @@ class CORE_EXPORT QgsPointLocator : public QObject
//! find out if the point is in any polygons
MatchList pointInPolygon( const QgsPointXY &point );
//
/**
* Return how many geometries are cached in the index
* \since QGIS 2.14
@ -278,11 +285,15 @@ class CORE_EXPORT QgsPointLocator : public QObject
//! flag whether the layer is currently empty (i.e. mRTree is null but it is not necessary to rebuild it)
bool mIsEmptyLayer;
QgsFeatureIds mFeatureIds;
//! R-tree containing spatial index
QgsCoordinateTransform mTransform;
QgsVectorLayer *mLayer = nullptr;
QgsRectangle *mExtent = nullptr;
std::unique_ptr<QgsRenderContext> mContext;
friend class QgsPointLocator_VisitorNearestVertex;
friend class QgsPointLocator_VisitorNearestEdge;
friend class QgsPointLocator_VisitorArea;

View File

@ -19,10 +19,12 @@
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include "qgslogger.h"
#include "qgsrenderer.h"
QgsSnappingUtils::QgsSnappingUtils( QObject *parent )
QgsSnappingUtils::QgsSnappingUtils( QObject *parent, bool enableSnappingForInvisibleFeature )
: QObject( parent )
, mSnappingConfig( QgsProject::instance() )
, mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature )
{
}
@ -92,7 +94,6 @@ bool QgsSnappingUtils::isIndexPrepared( QgsVectorLayer *vl, const QgsRectangle &
return ( mStrategy == IndexHybrid || mStrategy == IndexExtent ) && loc->hasIndex() && ( !loc->extent() || loc->extent()->contains( aoi ) ); // the index - even if it exists - is not suitable
}
static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY &pt, const QgsPointLocator::MatchList &segments )
{
if ( segments.isEmpty() )
@ -156,9 +157,9 @@ static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY
return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP );
}
static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointLocator::Match &candidateMatch, double maxDistance )
{
// is candidate match relevant?
if ( !candidateMatch.isValid() || candidateMatch.distance() > maxDistance )
return;
@ -174,7 +175,6 @@ static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointL
bestMatch = candidateMatch; // the other match is better!
}
static void _updateBestMatch( QgsPointLocator::Match &bestMatch, const QgsPointXY &pointMap, QgsPointLocator *loc, QgsPointLocator::Types type, double tolerance, QgsPointLocator::MatchFilter *filter )
{
if ( type & QgsPointLocator::Vertex )
@ -329,7 +329,6 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPointXY &pointMap,
return QgsPointLocator::Match();
}
void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers )
{
if ( mIsIndexing )
@ -341,6 +340,7 @@ void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers
Q_FOREACH ( const LayerAndAreaOfInterest &entry, layers )
{
QgsVectorLayer *vl = entry.first;
if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
continue;
@ -359,7 +359,12 @@ void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers
QgsVectorLayer *vl = entry.first;
QTime tt;
tt.start();
QgsPointLocator *loc = locatorForLayer( vl );
if ( !mEnableSnappingForInvisibleFeature )
loc->setRenderContext( QgsRenderContext::fromMapSettings( mMapSettings ) );
if ( mStrategy == IndexExtent )
{
QgsRectangle rect( mMapSettings.extent() );
@ -428,6 +433,11 @@ QgsSnappingConfig QgsSnappingUtils::config() const
return mSnappingConfig;
}
void QgsSnappingUtils::setEnableSnappingForInvisibleFeature( bool enable )
{
mEnableSnappingForInvisibleFeature = enable;
}
void QgsSnappingUtils::setConfig( const QgsSnappingConfig &config )
{
if ( mSnappingConfig == config )

View File

@ -53,7 +53,7 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
public:
//! Constructor for QgsSnappingUtils
QgsSnappingUtils( QObject *parent SIP_TRANSFERTHIS = nullptr );
QgsSnappingUtils( QObject *parent SIP_TRANSFERTHIS = nullptr, bool enableSnappingForInvisibleFeature = true );
~QgsSnappingUtils() override;
// main actions
@ -159,6 +159,15 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
*/
QgsSnappingConfig config() const;
/**
* Set if invisible features must be snapped or not.
*
* \param enable Enable or not this feature
*
* \since QGIS 3.2
*/
void setEnableSnappingForInvisibleFeature( bool enable );
public slots:
/**
@ -242,6 +251,10 @@ class CORE_EXPORT QgsSnappingUtils : public QObject
//! internal flag that an indexing process is going on. Prevents starting two processes in parallel.
bool mIsIndexing = false;
//! Disable or not the snapping on all features. By default is always true except for non visible features on map canvas.
bool mEnableSnappingForInvisibleFeature = true;
};

View File

@ -16,12 +16,13 @@
#include "qgsmapcanvas.h"
#include "qgsvectorlayer.h"
#include "qgssettings.h"
#include <QApplication>
#include <QProgressDialog>
QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObject *parent )
: QgsSnappingUtils( parent )
: QgsSnappingUtils( parent, QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() )
, mCanvas( canvas )
{
@ -30,6 +31,7 @@ QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObj
connect( canvas, &QgsMapCanvas::layersChanged, this, &QgsMapCanvasSnappingUtils::canvasMapSettingsChanged );
connect( canvas, &QgsMapCanvas::currentLayerChanged, this, &QgsMapCanvasSnappingUtils::canvasCurrentLayerChanged );
connect( canvas, &QgsMapCanvas::transformContextChanged, this, &QgsMapCanvasSnappingUtils::canvasTransformContextChanged );
connect( canvas, &QgsMapCanvas::mapToolSet, this, &QgsMapCanvasSnappingUtils::canvasMapToolChanged );
canvasMapSettingsChanged();
canvasCurrentLayerChanged();
}
@ -37,6 +39,7 @@ QgsMapCanvasSnappingUtils::QgsMapCanvasSnappingUtils( QgsMapCanvas *canvas, QObj
void QgsMapCanvasSnappingUtils::canvasMapSettingsChanged()
{
setMapSettings( mCanvas->mapSettings() );
setEnableSnappingForInvisibleFeature( QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() );
}
void QgsMapCanvasSnappingUtils::canvasTransformContextChanged()
@ -51,6 +54,11 @@ void QgsMapCanvasSnappingUtils::canvasCurrentLayerChanged()
setCurrentLayer( qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ) );
}
void QgsMapCanvasSnappingUtils::canvasMapToolChanged()
{
setEnableSnappingForInvisibleFeature( QgsSettings().value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() );
}
void QgsMapCanvasSnappingUtils::prepareIndexStarting( int count )
{
QApplication::setOverrideCursor( Qt::WaitCursor );

View File

@ -18,6 +18,8 @@
#include "qgssnappingutils.h"
#include "qgis_gui.h"
#include "qgsmaptool.h"
class QgsMapCanvas;
class QProgressDialog;
@ -42,6 +44,7 @@ class GUI_EXPORT QgsMapCanvasSnappingUtils : public QgsSnappingUtils
void canvasMapSettingsChanged();
void canvasTransformContextChanged();
void canvasCurrentLayerChanged();
void canvasMapToolChanged();
private:
QgsMapCanvas *mCanvas = nullptr;

View File

@ -320,7 +320,7 @@
<item>
<widget class="QStackedWidget" name="mOptionsStackedWidget">
<property name="currentIndex">
<number>7</number>
<number>8</number>
</property>
<widget class="QWidget" name="mOptionsPageGeneral">
<layout class="QVBoxLayout" name="verticalLayout_3">
@ -349,8 +349,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>843</width>
<height>839</height>
<width>411</width>
<height>662</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_28">
@ -654,12 +654,12 @@
</property>
<item row="1" column="0">
<widget class="QCheckBox" name="mDataSourceManagerNonModal">
<property name="text">
<string>Modeless data source manager dialog</string>
</property>
<property name="toolTip">
<string>A modeless dialog allows you to interact with QGIS main window and dialogs.</string>
</property>
<property name="text">
<string>Modeless data source manager dialog</string>
</property>
</widget>
</item>
<item row="1" column="1">
@ -1040,8 +1040,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>843</width>
<height>1110</height>
<width>437</width>
<height>1011</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_22">
@ -1576,8 +1576,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>826</height>
<width>443</width>
<height>312</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_15">
@ -1743,8 +1743,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>826</height>
<width>393</width>
<height>648</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_27">
@ -2111,8 +2111,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>843</width>
<height>1110</height>
<width>541</width>
<height>866</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_22">
@ -2862,8 +2862,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>826</height>
<width>449</width>
<height>195</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_25">
@ -3119,8 +3119,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>826</height>
<width>515</width>
<height>527</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_30">
@ -3563,8 +3563,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>826</height>
<width>124</width>
<height>230</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_46">
@ -3731,8 +3731,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>572</width>
<height>889</height>
<width>862</width>
<height>838</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_31">
@ -3954,39 +3954,75 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<property name="title">
<string>Snapping</string>
</property>
<layout class="QGridLayout" name="_10">
<item row="6" column="5">
<widget class="QgsColorButton" name="mSnappingMarkerColorButton"/>
</item>
<item row="1" column="1" colspan="6">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="4">
<widget class="QCheckBox" name="mSnappingEnabledDefault">
<property name="text">
<string>Enable snapping by default</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_49">
<property name="text">
<string>Display main dialog as (restart required)</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Snapping marker color</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="1" column="0">
<widget class="QLabel" name="mDefaultSnapModeLabel">
<property name="text">
<string>Default snap mode</string>
</property>
</widget>
</item>
<item row="3" column="6">
<item row="1" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>273</width>
<height>19</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="mDefaultSnapModeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mDefaultSnappingToleranceTextLabel">
<property name="text">
<string>Default snapping tolerance</string>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>241</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QDoubleSpinBox" name="mDefaultSnappingToleranceSpinBox">
<property name="decimals">
<number>5</number>
</property>
<property name="maximum">
<double>99999999.989999994635582</double>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QComboBox" name="mDefaultSnappingToleranceComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -4006,7 +4042,14 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
</item>
</widget>
</item>
<item row="4" column="4">
<item row="3" column="0">
<widget class="QLabel" name="mVertexSearchRadiusVertexEditLabel">
<property name="text">
<string>Search radius for vertex edits</string>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -4019,30 +4062,17 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
</property>
</spacer>
</item>
<item row="3" column="3" colspan="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="3" column="2">
<widget class="QDoubleSpinBox" name="mSearchRadiusVertexEditSpinBox">
<property name="decimals">
<number>5</number>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>241</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="5" colspan="2">
<widget class="QComboBox" name="mDefaultSnapModeComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="maximum">
<double>99999999.989999994635582</double>
</property>
</widget>
</item>
<item row="4" column="6">
<item row="3" column="3">
<widget class="QComboBox" name="mSearchRadiusVertexEditComboBox">
<item>
<property name="text">
@ -4056,63 +4086,40 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
</item>
</widget>
</item>
<item row="4" column="5">
<widget class="QDoubleSpinBox" name="mSearchRadiusVertexEditSpinBox">
<property name="decimals">
<number>5</number>
</property>
<property name="maximum">
<double>99999999.989999994635582</double>
<item row="4" column="0">
<widget class="QLabel" name="label_49">
<property name="text">
<string>Display main dialog as (restart required)</string>
</property>
</widget>
</item>
<item row="3" column="5">
<widget class="QDoubleSpinBox" name="mDefaultSnappingToleranceSpinBox">
<property name="decimals">
<number>5</number>
</property>
<property name="maximum">
<double>99999999.989999994635582</double>
</property>
</widget>
</item>
<item row="2" column="2" colspan="3">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>311</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="5" colspan="2">
<item row="4" column="2">
<widget class="QComboBox" name="mSnappingMainDialogComboBox"/>
</item>
<item row="4" column="1" colspan="3">
<widget class="QLabel" name="mVertexSearchRadiusVertexEditLabel">
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Search radius for vertex edits</string>
<string>Snapping marker color</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="mDefaultSnappingToleranceTextLabel">
<property name="text">
<string>Default snapping tolerance</string>
</property>
</widget>
<item row="5" column="2">
<widget class="QgsColorButton" name="mSnappingMarkerColorButton"/>
</item>
<item row="7" column="1" colspan="6">
<item row="6" column="0" colspan="4">
<widget class="QCheckBox" name="mSnappingTooltipsCheckbox">
<property name="text">
<string>Show snapping tooltips</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="QCheckBox" name="mEnableSnappingOnInvisibleFeatureCheckbox">
<property name="text">
<string>Enable snapping on invisible features (not shown on the map canvas)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -4326,8 +4333,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>509</width>
<height>623</height>
<width>390</width>
<height>539</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_39">
@ -4595,8 +4602,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>435</width>
<height>402</height>
<width>862</width>
<height>838</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
@ -4764,8 +4771,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>655</width>
<height>768</height>
<width>862</width>
<height>838</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33">
@ -5533,7 +5540,6 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<tabstop>mSearchRadiusVertexEditComboBox</tabstop>
<tabstop>mSnappingMainDialogComboBox</tabstop>
<tabstop>mSnappingMarkerColorButton</tabstop>
<tabstop>mSnappingTooltipsCheckbox</tabstop>
<tabstop>mMarkersOnlyForSelectedCheckBox</tabstop>
<tabstop>mMarkerStyleComboBox</tabstop>
<tabstop>mMarkerSizeSpinBox</tabstop>
@ -5609,7 +5615,6 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
<include location="../../images/images.qrc"/>
</resources>
<connections>
<connection>

View File

@ -24,7 +24,8 @@
#include "qgsproject.h"
#include "qgssnappingutils.h"
#include "qgssnappingconfig.h"
#include "qgscategorizedsymbolrenderer.h"
#include "qgssettings.h"
struct FilterExcludePoint : public QgsPointLocator::MatchFilter
{
@ -44,6 +45,7 @@ class TestQgsSnappingUtils : public QObject
private:
QgsVectorLayer *mVL = nullptr;
QgsFeature f1, f2;
private slots:
void initTestCase()
@ -60,16 +62,29 @@ class TestQgsSnappingUtils : public QObject
// \ |
// \|
// + (1,0)
mVL = new QgsVectorLayer( QStringLiteral( "Polygon" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) );
QgsFeature ff( 0 );
mVL = new QgsVectorLayer( QStringLiteral( "Polygon?field=fld:int" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) );
int idx = mVL->fields().indexFromName( QStringLiteral( "fld" ) );
QVERIFY( idx != -1 );
f1.initAttributes( 1 );
f2.initAttributes( 1 );
QgsPolygonXY polygon;
QgsPolylineXY polyline;
polyline << QgsPointXY( 0, 1 ) << QgsPointXY( 1, 0 ) << QgsPointXY( 1, 1 ) << QgsPointXY( 0, 1 );
polygon << polyline;
QgsGeometry polygonGeom = QgsGeometry::fromPolygonXY( polygon );
ff.setGeometry( polygonGeom );
f1.setGeometry( polygonGeom );
f1.setAttribute( idx, QVariant( 2 ) );
polyline << QgsPointXY( 10, 11 ) << QgsPointXY( 11, 10 ) << QgsPointXY( 11, 11 ) << QgsPointXY( 10, 11 );
polygon << polyline;
polygonGeom = QgsGeometry::fromPolygonXY( polygon );
f2.setGeometry( polygonGeom );
f2.setAttribute( idx, QVariant( 20 ) );
QgsFeatureList flist;
flist << ff;
flist << f1 << f2;
mVL->dataProvider()->addFeatures( flist );
QgsProject::instance()->addMapLayer( mVL );
@ -82,6 +97,13 @@ class TestQgsSnappingUtils : public QObject
void testSnapModeCurrent()
{
QgsSymbol *s1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry );
QgsSymbol *s2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry );
QgsRendererCategory c1( 2, s1, "f1", true );
QgsRendererCategory c2( 20, s2, "f2", false );
QgsCategoryList cl;
cl << c1 << c2;
QgsMapSettings mapSettings;
mapSettings.setOutputSize( QSize( 100, 100 ) );
mapSettings.setExtent( QgsRectangle( 0, 0, 1, 1 ) );
@ -89,6 +111,7 @@ class TestQgsSnappingUtils : public QObject
QgsSnappingUtils u;
u.setMapSettings( mapSettings );
u.setEnableSnappingForInvisibleFeature( false );
u.setCurrentLayer( mVL );
// first try with no snapping enabled
@ -99,7 +122,7 @@ class TestQgsSnappingUtils : public QObject
snappingConfig.setMode( QgsSnappingConfig::ActiveLayer );
u.setConfig( snappingConfig );
QgsPointLocator::Match m0 = u.snapToMap( QPoint( 100, 100 ) );
QgsPointLocator::Match m0 = u.snapToMap( QPoint( 2, 2 ) );
QVERIFY( !m0.isValid() );
QVERIFY( !m0.hasVertex() );
@ -108,10 +131,16 @@ class TestQgsSnappingUtils : public QObject
snappingConfig.setType( QgsSnappingConfig::Vertex );
u.setConfig( snappingConfig );
QgsPointLocator::Match m = u.snapToMap( QPoint( 100, 100 ) );
QgsPointLocator::Match m = u.snapToMap( QPoint( 11, 11 ) );
QVERIFY( !m.isValid() );
QVERIFY( !m.hasVertex() );
u.setEnableSnappingForInvisibleFeature( true );
mVL->styleChanged();
m = u.snapToMap( QPoint( 2, 2 ) );
QVERIFY( m.isValid() );
QVERIFY( m.hasVertex() );
QCOMPARE( m.point(), QgsPointXY( 1, 0 ) );
QCOMPARE( m.point(), QgsPointXY( 0, 1 ) );
QgsPointLocator::Match m2 = u.snapToMap( QPoint( 0, 100 ) );
QVERIFY( !m2.isValid() );
@ -127,6 +156,8 @@ class TestQgsSnappingUtils : public QObject
FilterExcludePoint myFilter( QgsPointXY( 1, 0 ) );
QgsPointLocator::Match m3 = u.snapToMap( QPoint( 100, 100 ), &myFilter );
QVERIFY( !m3.isValid() );
}
void testSnapModeAll()