Skip to content
Permalink
Browse files

Add option to control the tolerance of streamed digitizing

  • Loading branch information
nyalldawson committed Mar 11, 2021
1 parent bffa42a commit 526010589b6444dce0c46aa2cfd50de7fe12bfd2
@@ -472,6 +472,7 @@ target_include_directories(qgis_app PUBLIC
${CMAKE_SOURCE_DIR}/src/app/pluginmanager
${CMAKE_SOURCE_DIR}/src/app/gps
${CMAKE_SOURCE_DIR}/src/app/dwg
${CMAKE_SOURCE_DIR}/src/app/maptools
${CMAKE_SOURCE_DIR}/src/app/mesh
${CMAKE_SOURCE_DIR}/src/app/locator
${CMAKE_SOURCE_DIR}/src/app/pointcloud
@@ -68,6 +68,50 @@
#include "qgsmaptoolchangelabelproperties.h"
#include "qgsmaptoolpinlabels.h"
#include "qgsmaptooloffsetpointsymbol.h"
#include "qgsspinbox.h"

//
// QgsStreamDigitizingSettingsAction
//

QgsStreamDigitizingSettingsAction::QgsStreamDigitizingSettingsAction( QWidget *parent )
: QWidgetAction( parent )
{
QGridLayout *gLayout = new QGridLayout();
gLayout->setContentsMargins( 3, 2, 3, 2 );

QgsSettings settings;
int defaultTolerance = settings.value( QStringLiteral( "/qgis/digitizing/stream_tolerance" ), 2 ).toInt();

mStreamToleranceSpinBox = new QgsSpinBox();
mStreamToleranceSpinBox->setSuffix( tr( "px" ) );
mStreamToleranceSpinBox->setKeyboardTracking( false );
mStreamToleranceSpinBox->setRange( 1, 200 );
mStreamToleranceSpinBox->setWrapping( false );
mStreamToleranceSpinBox->setSingleStep( 1 );
mStreamToleranceSpinBox->setClearValue( 2 );
mStreamToleranceSpinBox->setValue( defaultTolerance );

QLabel *label = new QLabel( tr( "Tolerance" ) );
gLayout->addWidget( label, 1, 0 );
gLayout->addWidget( mStreamToleranceSpinBox, 1, 1 );
connect( mStreamToleranceSpinBox, qgis::overload<int>::of( &QgsSpinBox::valueChanged ), this, [ = ]( int value )
{
QgsSettings settings;
settings.setValue( QStringLiteral( "/qgis/digitizing/stream_tolerance" ), value );
} );

QWidget *w = new QWidget();
w->setLayout( gLayout );
setDefaultWidget( w );
}

QgsStreamDigitizingSettingsAction::~QgsStreamDigitizingSettingsAction() = default;


//
// QgsAppMapTools
//

QgsAppMapTools::QgsAppMapTools( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDock )
{
@@ -133,6 +177,8 @@ QgsAppMapTools::QgsAppMapTools( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockW
mTools.insert( Tool::MoveLabel, new QgsMapToolMoveLabel( canvas, cadDock ) );
mTools.insert( Tool::RotateLabel, new QgsMapToolRotateLabel( canvas, cadDock ) );
mTools.insert( Tool::ChangeLabelProperties, new QgsMapToolChangeLabelProperties( canvas, cadDock ) );

mStreamDigitizingSettingsAction = new QgsStreamDigitizingSettingsAction();
}

QgsAppMapTools::~QgsAppMapTools()
@@ -160,3 +206,8 @@ QList<QgsMapToolCapture *> QgsAppMapTools::captureTools() const
return res;
}

QWidgetAction *QgsAppMapTools::streamDigitizingSettingsAction()
{
return mStreamDigitizingSettingsAction;
}

@@ -19,12 +19,29 @@
#include <QList>
#include <QHash>
#include <QPointer>
#include <QWidgetAction>


class QgsMapTool;
class QgsMapToolCapture;

class QgsMapCanvas;
class QgsAdvancedDigitizingDockWidget;
class QgsSpinBox;

class QgsStreamDigitizingSettingsAction: public QWidgetAction
{
Q_OBJECT

public:

QgsStreamDigitizingSettingsAction( QWidget *parent = nullptr );
~QgsStreamDigitizingSettingsAction() override;

private:
QgsSpinBox *mStreamToleranceSpinBox = nullptr;
};


class QgsAppMapTools
{
@@ -119,9 +136,15 @@ class QgsAppMapTools
*/
QList< QgsMapToolCapture * > captureTools() const;

/**
* Returns the stream digitizing settings action;
*/
QWidgetAction *streamDigitizingSettingsAction();

private:

QHash< Tool, QPointer< QgsMapTool > > mTools;
QgsStreamDigitizingSettingsAction *mStreamDigitizingSettingsAction = nullptr;

};

@@ -1049,9 +1049,13 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
functionProfile( &QgisApp::createMenus, this, QStringLiteral( "Create menus" ) );
functionProfile( &QgisApp::createActions, this, QStringLiteral( "Create actions" ) );
functionProfile( &QgisApp::createActionGroups, this, QStringLiteral( "Create action group" ) );

// create tools
mMapTools = std::make_unique< QgsAppMapTools >( mMapCanvas, mAdvancedDigitizingDockWidget );

functionProfile( &QgisApp::createToolBars, this, QStringLiteral( "Toolbars" ) );
functionProfile( &QgisApp::createStatusBar, this, QStringLiteral( "Status bar" ) );
functionProfile( &QgisApp::createCanvasTools, this, QStringLiteral( "Create canvas tools" ) );
functionProfile( &QgisApp::setupCanvasTools, this, QStringLiteral( "Create canvas tools" ) );
const QList< QgsMapToolCapture * > captureTools = mMapTools->captureTools();
for ( QgsMapToolCapture *tool : captureTools )
{
@@ -1718,6 +1722,8 @@ QgisApp::~QgisApp()
delete mQgisInterface;
delete mStyleSheetBuilder;

if ( QgsMapTool *tool = mMapCanvas->mapTool() )
mMapCanvas->unsetMapTool( tool );
mMapTools.reset();

delete mpMaptip;
@@ -3203,6 +3209,13 @@ void QgisApp::createToolBars()
static_cast< void ( QgsDoubleSpinBox::* )( double ) >( &QgsDoubleSpinBox::valueChanged ),
this, [ = ]( double v ) { mTracer->setOffset( v ); } );

QToolButton *bt = new QToolButton();
bt->setPopupMode( QToolButton::MenuButtonPopup );
bt->addAction( mActionStreamDigitize );
bt->addAction( mMapTools->streamDigitizingSettingsAction() );
bt->setDefaultAction( mActionStreamDigitize );
mAdvancedDigitizeToolBar->insertWidget( mAdvancedDigitizeToolBar->actions().at( 0 ), bt );

QList<QAction *> toolbarMenuActions;
// Set action names so that they can be used in customization
const auto constToolbarMenuToolBars = toolbarMenuToolBars;
@@ -3218,7 +3231,7 @@ void QgisApp::createToolBars()
mToolbarMenu->addActions( toolbarMenuActions );

// advanced selection tool button
QToolButton *bt = new QToolButton( mSelectionToolBar );
bt = new QToolButton( mSelectionToolBar );
bt->setPopupMode( QToolButton::MenuButtonPopup );
bt->addAction( mActionSelectByForm );
bt->addAction( mActionSelectByExpression );
@@ -4226,11 +4239,8 @@ void QgisApp::setupConnections()
connect( mLayoutsMenu, &QMenu::aboutToShow, this, &QgisApp::layoutsMenuAboutToShow );
}

void QgisApp::createCanvasTools()
void QgisApp::setupCanvasTools()
{
// create tools
mMapTools = std::make_unique< QgsAppMapTools >( mMapCanvas, mAdvancedDigitizingDockWidget );

mMapTools->mapTool( QgsAppMapTools::ZoomIn )->setAction( mActionZoomIn );
mMapTools->mapTool( QgsAppMapTools::ZoomOut )->setAction( mActionZoomOut );
connect( mMapTools->mapTool< QgsMapToolPan >( QgsAppMapTools::Pan ), &QgsMapToolPan::panDistanceBearingChanged, this, &QgisApp::showPanMessage );
@@ -10114,6 +10124,12 @@ void QgisApp::snappingOptions()

void QgisApp::enableDigitizeWithCurve( bool enable )
{
if ( enable && mActionStreamDigitize->isChecked() )
{
mActionStreamDigitize->setChecked( false );
enableStreamDigitizing( false );
}

const QList< QgsMapToolCapture * > captureTools = mMapTools->captureTools();
for ( QgsMapToolCapture *tool : captureTools )
{
@@ -10126,6 +10142,12 @@ void QgisApp::enableDigitizeWithCurve( bool enable )

void QgisApp::enableStreamDigitizing( bool enable )
{
if ( enable && mActionDigitizeWithCurve->isChecked() )
{
mActionDigitizeWithCurve->setChecked( false );
enableDigitizeWithCurve( false );
}

const QList< QgsMapToolCapture * > captureTools = mMapTools->captureTools();
for ( QgsMapToolCapture *tool : captureTools )
{
@@ -2235,7 +2235,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
void setupConnections();
void initLayerTreeView();
void createOverview();
void createCanvasTools();
void setupCanvasTools();
void createMapTips();
void createDecorations();
void init3D();
@@ -385,6 +385,15 @@ void QgsMapToolCapture::cadCanvasMoveEvent( QgsMapMouseEvent *e )

if ( mStreamingEnabled )
{
if ( ! mCaptureCurve.isEmpty() )
{
QgsSettings settings;
const int tolerance = settings.value( QStringLiteral( "/qgis/digitizing/stream_tolerance" ), 2 ).toInt();
QgsPoint prevPoint = mCaptureCurve.curveAt( mCaptureCurve.nCurves() - 1 )->endPoint();
if ( QgsPointXY( toCanvasCoordinates( toMapCoordinates( mCanvas->currentLayer(), prevPoint ) ) ).distance( e->pos() ) < tolerance )
return;
}

mAllowAddingStreamingPoints = true;
addVertex( mapPoint );
mAllowAddingStreamingPoints = false;
@@ -521,7 +521,6 @@
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="mActionStreamDigitize"/>
<addaction name="mActionDigitizeWithCurve"/>
<addaction name="mActionRotateFeature"/>
<addaction name="mActionScaleFeature"/>
@@ -77,6 +77,7 @@ class TestQgsMapToolAddFeatureLine : public QObject
void testCompoundCurve();
void testStream();
void testUndo();
void testStreamTolerance();

private:
QgisApp *mQgisApp = nullptr;
@@ -112,6 +113,8 @@ void TestQgsMapToolAddFeatureLine::initTestCase()
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
QgsSettings settings;
settings.clear();

mQgisApp = new QgisApp();

@@ -989,5 +992,55 @@ void TestQgsMapToolAddFeatureLine::testUndo()
mCaptureTool->setStreamDigitizingEnabled( false );
}

void TestQgsMapToolAddFeatureLine::testStreamTolerance()
{
// test streaming mode digitizing with tolerance
QgsSettings settings;
settings.setValue( QStringLiteral( "/qgis/digitizing/stream_tolerance" ), 10 );

TestQgsMapToolAdvancedDigitizingUtils utils( mCaptureTool );

mCanvas->setCurrentLayer( mLayerLine );

QSet<QgsFeatureId> oldFids = utils.existingFeatureIds();

QgsSnappingConfig cfg = mCanvas->snappingUtils()->config();
cfg.setEnabled( false );
mCanvas->snappingUtils()->setConfig( cfg );

oldFids = utils.existingFeatureIds();
utils.mouseClick( 5, 6.5, Qt::LeftButton );
utils.mouseClick( 6.25, 6.5, Qt::LeftButton );
utils.mouseClick( 6.75, 6.5, Qt::LeftButton );

mCaptureTool->setStreamDigitizingEnabled( true );
utils.mouseMove( 7.0, 6.6 );
utils.mouseMove( 7.1, 6.7 );
utils.mouseMove( 7.2, 6.6 );
utils.mouseMove( 7.3, 6.5 );
utils.mouseMove( 7.5, 6.9 );
utils.mouseMove( 7.6, 6.3 );
utils.mouseClick( 7.75, 6.5, Qt::LeftButton );
mCaptureTool->setStreamDigitizingEnabled( false );
utils.mouseClick( 7.5, 5.0, Qt::LeftButton );
mCaptureTool->setStreamDigitizingEnabled( true );
utils.mouseMove( 7.4, 5.0 );
utils.mouseMove( 7.3, 5.1 );
utils.mouseMove( 7.2, 5.0 );
utils.mouseMove( 7.1, 4.9 );

// check capture curve initially -- the streamed sections MUST become their own curve in the geometry, and not be compined with the straight line segments!
QCOMPARE( mCaptureTool->captureCurve()->asWkt( 2 ), QStringLiteral( "CompoundCurve ((5 6.5, 6.25 6.5, 6.75 6.5),(6.75 6.5, 7 6.59, 7.2 6.59, 7.5 6.91, 7.59 6.3),(7.59 6.3, 7.5 5),(7.5 5, 7.3 5.09, 7.09 4.91))" ) );
utils.mouseClick( 7.0, 5.0, Qt::RightButton );
mCaptureTool->setStreamDigitizingEnabled( false );

QgsFeatureId newFid = utils.newFeatureId( oldFids );

QString wkt = "LineString (5 6.5, 6.25 6.5, 6.75 6.5, 7 6.59375, 7.203125 6.59375, 7.5 6.90625, 7.59375 6.296875, 7.5 5, 7.296875 5.09375, 7.09375 4.90625)";
QCOMPARE( mLayerLine->getFeature( newFid ).geometry(), QgsGeometry::fromWkt( wkt ) );

mLayerLine->undoStack()->undo();
}

QGSTEST_MAIN( TestQgsMapToolAddFeatureLine )
#include "testqgsmaptooladdfeatureline.moc"

0 comments on commit 5260105

Please sign in to comment.